Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1aae7be
add autodiff capability
MialLewis May 6, 2025
7abffb7
use autodiff for phi calc
MialLewis May 7, 2025
c17ac36
move matrix workspace evaluation to ErrorProp class
MialLewis May 8, 2025
bb5aed1
add new error propagation to fp and fa
MialLewis May 8, 2025
5618276
add new error prop to calculateTPMO
MialLewis May 8, 2025
f020f2d
add error prop to remaining variables
MialLewis May 8, 2025
8cc319e
refactor code to reduce repetition
MialLewis May 8, 2025
4963cd4
suppress spurious cppcheck suppressions
MialLewis May 8, 2025
de8d735
update PolEff system tests
MialLewis May 9, 2025
39c7f46
add warning messages regarding errors when calculating efficiencies u…
MialLewis May 9, 2025
184456c
update PolarizationEfficienciesWildesTest to check errors
MialLewis May 9, 2025
64604ad
add tests for workspace error prop
MialLewis May 9, 2025
ebc1b34
remove unnecessary vars
MialLewis May 9, 2025
87a7f2c
remove unused var from test
MialLewis May 9, 2025
4dbf94f
correct helper tests
MialLewis May 9, 2025
978f31c
add ability to set workspace distribution in ErrorProp, also add test…
MialLewis May 12, 2025
b05f9de
test for distribution flag
MialLewis May 13, 2025
f01ae16
stop comp between size_t and int
MialLewis May 13, 2025
c143592
Remove unused type alias
MialLewis May 13, 2025
3705545
add test data and correct flipper config in system tests
MialLewis May 16, 2025
1482c70
add non mag test data
MialLewis May 16, 2025
77f62d2
respond to pr comments 1
MialLewis May 20, 2025
ec82c91
respond to PR comments 2
MialLewis May 21, 2025
2e641b4
slight fn refactor to reduce duplication
MialLewis May 22, 2025
ddc014d
update system test data again
MialLewis May 22, 2025
a62caec
remove unintended change
MialLewis May 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@

#pragma once

#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/WorkspaceGroup.h"
#include "MantidAlgorithms/DllConfig.h"
#include "MantidKernel/MultiThreaded.h"
#include <Eigen/Dense>
#include <optional>
#include <unsupported/Eigen/AutoDiff>

namespace Mantid::Algorithms {
namespace PolarizationCorrectionsHelpers {
Expand Down Expand Up @@ -66,4 +70,109 @@ MANTID_ALGORITHMS_DLL const std::string &getORSONotationForSpinState(const std::
MANTID_ALGORITHMS_DLL void addORSOLogForSpinState(const Mantid::API::MatrixWorkspace_sptr &ws,
const std::string &spinState);
} // namespace SpinStatesORSO

namespace Arithmetic {

template <size_t N> class ErrorTypeHelper {
public:
using DerType = Eigen::Matrix<double, N, 1>;
using InputArray = DerType;
using ADScalar = Eigen::AutoDiffScalar<DerType>;
};

template <size_t N, typename Func> class ErrorPropagation {
public:
using Types = ErrorTypeHelper<N>;
using DerType = Types::DerType;
using ADScalar = Types::ADScalar;
using InputArray = Types::InputArray;
ErrorPropagation(Func func) : computeFunc(std::move(func)) {}

struct AutoDevResult {
double value;
double error;
Eigen::Array<double, N, 1> derivatives;
};

AutoDevResult evaluate(const InputArray &values, const InputArray &errors) const {
std::array<ADScalar, N> x;
for (size_t i = 0; i < N; ++i) {
x[i] = ADScalar(values[i], DerType::Unit(N, i));
}
const ADScalar y = computeFunc(x);
const auto &derivatives = y.derivatives();
return {y.value(), std::sqrt((derivatives.array().square() * errors.array().square()).sum()), derivatives};
}

template <std::same_as<API::MatrixWorkspace_sptr>... Ts>
API::MatrixWorkspace_sptr evaluateWorkspaces(const bool outputWorkspaceDistribution, Ts... args) const {
return evaluateWorkspacesImpl(outputWorkspaceDistribution, std::forward<Ts>(args)...);
}

template <std::same_as<API::MatrixWorkspace_sptr>... Ts>
API::MatrixWorkspace_sptr evaluateWorkspaces(Ts... args) const {
return evaluateWorkspacesImpl(std::nullopt, std::forward<Ts>(args)...);
}

private:
Func computeFunc;

template <std::same_as<API::MatrixWorkspace_sptr>... Ts>
API::MatrixWorkspace_sptr evaluateWorkspacesImpl(std::optional<bool> outputWorkspaceDistribution, Ts... args) const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  template <std::same_as<API::MatrixWorkspace_sptr>... Ts>
  API::MatrixWorkspace_sptr evaluateWorkspaces(Ts... args) const {
    return evaluateWorkspacesImpl(false,  std::forward<Ts>(args)...);

I think we can get away with just using a bool here?

Suggested change
API::MatrixWorkspace_sptr evaluateWorkspacesImpl(std::optional<bool> outputWorkspaceDistribution, Ts... args) const {
API::MatrixWorkspace_sptr evaluateWorkspacesImpl(bool outputWorkspaceDistribution, Ts... args) const {

Copy link
Contributor Author

@MialLewis MialLewis May 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I used a std::optional here is that I wanted the user to be able to do three things:

  1. Specify no value, keep the distribution as it is on the LHS workspace
  2. Specify false, set the distribution flag to false
  3. Specify true, set the distribution flag to true.

const auto firstWs = std::get<0>(std::forward_as_tuple(args...));
API::MatrixWorkspace_sptr outWs = firstWs->clone();

if (outWs->id() == "EventWorkspace") {
outWs = convertToWorkspace2D(outWs);
}

const size_t numSpec = outWs->getNumberHistograms();
const size_t specSize = outWs->blocksize();

// cppcheck-suppress unreadVariable
const bool isThreadSafe = Kernel::threadSafe((*args)..., *outWs);
// cppcheck-suppress unreadVariable
const bool specOverBins = numSpec > specSize;

PARALLEL_FOR_IF(isThreadSafe && specOverBins)
for (int64_t i = 0; i < static_cast<int64_t>(numSpec); i++) {
auto &yOut = outWs->mutableY(i);
auto &eOut = outWs->mutableE(i);

PARALLEL_FOR_IF(isThreadSafe && !specOverBins)
for (int64_t j = 0; j < static_cast<int64_t>(specSize); ++j) {
const auto result = evaluate(InputArray{args->y(i)[j]...}, InputArray(args->e(i)[j]...));
yOut[j] = result.value;
eOut[j] = result.error;
}
}

if (outputWorkspaceDistribution.has_value()) {
outWs->setDistribution(outputWorkspaceDistribution.value());
}
return outWs;
}

API::MatrixWorkspace_sptr runWorkspaceConversionAlg(const API::MatrixWorkspace_sptr &workspace,
const std::string &algName) const {
auto conversionAlg = API::AlgorithmManager::Instance().create(algName);
conversionAlg->initialize();
conversionAlg->setChild(true);
conversionAlg->setProperty("InputWorkspace", workspace);
conversionAlg->setProperty("OutputWorkspace", workspace->getName());
conversionAlg->execute();
return conversionAlg->getProperty("OutputWorkspace");
}

API::MatrixWorkspace_sptr convertToWorkspace2D(const API::MatrixWorkspace_sptr &workspace) const {
runWorkspaceConversionAlg(workspace, "ConvertToHistogram");
return runWorkspaceConversionAlg(workspace, "ConvertToMatrixWorkspace");
}
};

template <size_t N, typename Func> auto makeErrorPropagation(Func &&func) {
return ErrorPropagation<N, std::decay_t<Func>>(std::forward<Func>(func));
}

} // namespace Arithmetic
} // namespace Mantid::Algorithms
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/WorkspaceGroup.h"
#include "MantidAlgorithms/DllConfig.h"
#include <unordered_map>

namespace Mantid::Algorithms {

Expand Down Expand Up @@ -45,19 +46,12 @@ class MANTID_ALGORITHMS_DLL PolarizationEfficienciesWildes : public API::Algorit
/// Calculate Fp, Fa and Phi
void calculateFlipperEfficienciesAndPhi();

/// Calculate (2p-1) from Phi, Fp, Fa and the magnetic workspace intensities
MatrixWorkspace_sptr calculateTPMOFromPhi(const WorkspaceGroup_sptr &magWsGrp);
/// Calculate (2p-1) from intensities
MatrixWorkspace_sptr calculateTPMO();

/// Calculate the polarizer and/or analyser efficiencies, as requested
void calculatePolarizerAndAnalyserEfficiencies(const bool solveForP, const bool solveForA);

/// If either the polarizer or the analyser efficiency is known, use the relationship Phi = (2p-1)(2a-1) to solve for
/// the other efficiency
MatrixWorkspace_sptr solveForUnknownEfficiency(const MatrixWorkspace_sptr &knownEfficiency);

/// Solve for the unknown efficiency from either (2p-1) or (2a-1) using the relationship Phi = (2p-1)(2a-1)
MatrixWorkspace_sptr solveUnknownEfficiencyFromTXMO(const MatrixWorkspace_sptr &wsTXMO);

/// Set the algorithm outputs
void setOutputs();

Expand All @@ -68,10 +62,29 @@ class MANTID_ALGORITHMS_DLL PolarizationEfficienciesWildes : public API::Algorit
/// held by the property
void resetPropertyValue(const std::string &propertyName);

/// Populates the spin state workspaces map from ws group given key prefix.
void populateSpinStateWorkspaces(const WorkspaceGroup_sptr &wsGrp, const std::string &keyPrefix = "");

/// Populates the spin state workspaces map
void mapSpinStateWorkspaces();

// Convenience struct for handling of flipper workspaces
struct FlipperWorkspaces {
const MatrixWorkspace_sptr &ws00;
const MatrixWorkspace_sptr &ws01;
const MatrixWorkspace_sptr &ws10;
const MatrixWorkspace_sptr &ws11;
};

/// Access flipper workspaces in the spin state workspaces map
FlipperWorkspaces getFlipperWorkspaces(const bool mag = false);

MatrixWorkspace_sptr m_wsFp = nullptr;
MatrixWorkspace_sptr m_wsFa = nullptr;
MatrixWorkspace_sptr m_wsPhi = nullptr;
MatrixWorkspace_sptr m_wsP = nullptr;
MatrixWorkspace_sptr m_wsA = nullptr;
std::unordered_map<std::string, MatrixWorkspace_sptr> m_spinStateWorkspaces;
bool m_magWsProvided = false;
};
} // namespace Mantid::Algorithms
Loading