Skip to content

Commit b07fb99

Browse files
peterfpetersonmantid-builderpre-commit-ci[bot]rosswhitfieldMialLewis
authored
AlignAndFocusPowderSlim for NOMAD and POWGEN (#40664)
This pulls the following into `ornl-next` * #40642 * #40644 * #40645 * #40643 * #40657 * #40331 * #40633 --------- Co-authored-by: mantid-builder <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Ross Whitfield <[email protected]> Co-authored-by: MialLewis <[email protected]>
1 parent 03c2647 commit b07fb99

File tree

25 files changed

+759
-471
lines changed

25 files changed

+759
-471
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ repos:
6868
)$
6969
7070
- repo: https://github.com/astral-sh/ruff-pre-commit
71-
rev: v0.14.11
71+
rev: v0.14.13
7272
# ruff must appear before black in the list of hooks
7373
hooks:
7474
- id: ruff-check

Framework/CurveFitting/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ set(SRC_FILES
4141
src/FitMW.cpp
4242
src/EigenComplexMatrix.cpp
4343
src/EigenComplexVector.cpp
44+
src/EigenFunctions.cpp
4445
src/EigenMatrix.cpp
4546
src/EigenMatrixView.cpp
4647
src/EigenVector.cpp
@@ -347,6 +348,7 @@ set(INC_FILES
347348
inc/MantidCurveFitting/Functions/VesuvioResolution.h
348349
inc/MantidCurveFitting/Functions/Voigt.h
349350
inc/MantidCurveFitting/Functions/ConvTempCorrection.h
351+
inc/MantidCurveFitting/EigenFunctions.h
350352
inc/MantidCurveFitting/GSLFunctions.h
351353
inc/MantidCurveFitting/GeneralDomainCreator.h
352354
inc/MantidCurveFitting/HalfComplex.h
@@ -409,9 +411,9 @@ set(TEST_FILES
409411
EigenComplexVectorTest.h
410412
EigenFortranMatrixTest.h
411413
EigenFortranVectorTest.h
414+
EigenFunctionsTest.h
412415
EigenJacobianTest.h
413416
EigenMatrixTest.h
414-
EigenToGSLTest.h
415417
EigenVectorTest.h
416418
EigenViewTest.h
417419
FitMWTest.h
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Mantid Repository : https://github.com/mantidproject/mantid
2+
//
3+
// Copyright &copy; 2026 ISIS Rutherford Appleton Laboratory UKRI,
4+
// NScD Oak Ridge National Laboratory, European Spallation Source,
5+
// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6+
// SPDX - License - Identifier: GPL - 3.0 +
7+
#pragma once
8+
9+
#include "MantidCurveFitting/EigenMatrix.h"
10+
11+
namespace Mantid::CurveFitting {
12+
13+
MANTID_CURVEFITTING_DLL Eigen::MatrixXd covar_from_jacobian(const map_type &J, double epsrel);
14+
15+
} // namespace Mantid::CurveFitting

Framework/CurveFitting/inc/MantidCurveFitting/GSLFunctions.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,5 @@ int gsl_f(const gsl_vector *x, void *params, gsl_vector *f);
5050
int gsl_df(const gsl_vector *x, void *params, gsl_matrix *J);
5151
int gsl_fdf(const gsl_vector *x, void *params, gsl_vector *f, gsl_matrix *J);
5252

53-
/// take data from Eigen Vector and take a gsl view
54-
inline gsl_vector_view getGSLVectorView(vec_map_type &v) { return gsl_vector_view_array(v.data(), v.size()); }
55-
/// take data from an Eigen Matrix and return a transposed a gsl view.
56-
inline gsl_matrix_view getGSLMatrixView(map_type &tr) { return gsl_matrix_view_array(tr.data(), tr.cols(), tr.rows()); }
57-
58-
/// take const data from Eigen Vector and take a gsl view
59-
inline gsl_vector_const_view const getGSLVectorView_const(const vec_map_type v) {
60-
return gsl_vector_const_view_array(v.data(), v.size());
61-
}
62-
/// take data from a constEigen Matrix and return a transposed gsl view.
63-
inline gsl_matrix_const_view const getGSLMatrixView_const(const map_type m) {
64-
return gsl_matrix_const_view_array(m.data(), m.cols(), m.rows());
65-
}
66-
6753
} // namespace CurveFitting
6854
} // namespace Mantid

Framework/CurveFitting/src/CostFunctions/CostFuncFitting.cpp

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@
99
//----------------------------------------------------------------------
1010
#include "MantidCurveFitting/CostFunctions/CostFuncFitting.h"
1111
#include "MantidAPI/IConstraint.h"
12+
#include "MantidCurveFitting/EigenFunctions.h"
1213
#include "MantidCurveFitting/EigenJacobian.h"
13-
#include "MantidCurveFitting/GSLFunctions.h"
1414
#include "MantidCurveFitting/SeqDomain.h"
1515
#include "MantidKernel/Exception.h"
1616

17-
#include <gsl/gsl_multifit_nlin.h>
1817
#include <limits>
1918
#include <utility>
2019

@@ -132,15 +131,8 @@ void CostFuncFitting::calActiveCovarianceMatrix(EigenMatrix &covar, double epsre
132131
// calculate the derivatives
133132
m_function->functionDeriv(*m_domain, J);
134133

135-
// let the GSL to compute the covariance matrix
136-
EigenMatrix covarTr(covar.tr());
137-
EigenMatrix tempJTr;
138-
tempJTr = j.transpose();
139-
gsl_matrix_const_view tempJTrGSL = getGSLMatrixView_const(tempJTr.inspector());
140-
gsl_matrix_view covarTrGSL = getGSLMatrixView(covarTr.mutator());
141-
142-
gsl_multifit_covar(&tempJTrGSL.matrix, epsrel, &covarTrGSL.matrix);
143-
covar = covarTr.tr();
134+
// let Eigen compute the covariance matrix
135+
covar = covar_from_jacobian(j, epsrel);
144136
}
145137

146138
/** Calculates covariance matrix
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Mantid Repository : https://github.com/mantidproject/mantid
2+
//
3+
// Copyright &copy; 2026 ISIS Rutherford Appleton Laboratory UKRI,
4+
// NScD Oak Ridge National Laboratory, European Spallation Source,
5+
// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6+
// SPDX - License - Identifier: GPL - 3.0 +
7+
//----------------------------------------------------------------------
8+
// Includes
9+
//----------------------------------------------------------------------
10+
#include "MantidCurveFitting/EigenFunctions.h"
11+
12+
namespace Mantid::CurveFitting {
13+
14+
/** Mimics gsl_multifit_covar(J, epsrel, covar)
15+
* @param J :: Jacobian
16+
* @param epsrel :: relative threshold to decide rank deficiency
17+
* @return covariance matrix, rows/cols set to zero for dependent params
18+
*/
19+
Eigen::MatrixXd covar_from_jacobian(const map_type &J, double epsrel) {
20+
if (epsrel < 0.0) {
21+
throw std::invalid_argument("epsrel must be non-negative");
22+
}
23+
24+
const Eigen::Index nr = J.rows(); // nr = num rows
25+
const Eigen::Index nc = J.cols(); // nc = num cols
26+
if ((nc == 0) || (nr == 0))
27+
return Eigen::MatrixXd{};
28+
29+
// Pivoted QR Decomposition: JP = QR
30+
Eigen::ColPivHouseholderQR<Eigen::MatrixXd> qr(J);
31+
32+
// R is (nc x np) in general; the useful part for J^T J is (nc x nc) block
33+
Eigen::MatrixXd R = qr.matrixR().topLeftCorner(nc, nc).template triangularView<Eigen::Upper>().toDenseMatrix();
34+
35+
// Determine rank using the same rule as GSL: compare diag entries to |R_11|
36+
const double r11 = std::abs(R(0, 0));
37+
Eigen::Index rank = 0;
38+
if (r11 > 0.0) {
39+
for (Eigen::Index k = 0; k < nc; ++k) {
40+
if (std::abs(R(k, k)) > epsrel * r11) { // column considered linerally independant
41+
++rank;
42+
} else {
43+
break; // with pivoting, following first dependent subsequent cols should also be dependant
44+
}
45+
}
46+
} else {
47+
// R11 == 0, everything dependent
48+
rank = 0;
49+
}
50+
51+
// Build covariance in the pivoted parameter order
52+
Eigen::MatrixXd cov_pivot = Eigen::MatrixXd::Zero(nc, nc);
53+
54+
if (rank > 0) {
55+
// cov = (R^T R)^{-1} for the independent cols = R^{-1} R^{-T}
56+
const Eigen::MatrixXd R1 = R.topLeftCorner(rank, rank).template triangularView<Eigen::Upper>();
57+
Eigen::MatrixXd invR1 = R1.inverse();
58+
Eigen::MatrixXd cov1 = invR1 * invR1.transpose();
59+
cov_pivot.topLeftCorner(rank, rank) = cov1;
60+
}
61+
62+
// Unpivot back to original parameter order:
63+
// cov = (J^T J)^{-1} = P (R^T R)^{-1} P^T
64+
const Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic> P = qr.colsPermutation();
65+
Eigen::MatrixXd cov = P * cov_pivot * P.transpose();
66+
67+
return cov;
68+
}
69+
70+
} // namespace Mantid::CurveFitting

Framework/CurveFitting/src/FuncMinimizers/DampedGaussNewtonMinimizer.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
#include "MantidKernel/Logger.h"
1919

2020
#include <cmath>
21-
#include <gsl/gsl_blas.h>
2221

2322
namespace Mantid::CurveFitting::FuncMinimisers {
2423

@@ -121,9 +120,7 @@ bool DampedGaussNewtonMinimizer::iterate(size_t /*iteration*/) {
121120
// Try the stop condition
122121
EigenVector p(n);
123122
m_leastSquares->getParameters(p);
124-
gsl_vector_view dx_gsl = getGSLVectorView(dx.mutator());
125-
double dx_norm = gsl_blas_dnrm2(&dx_gsl.vector);
126-
return dx_norm >= m_relTol;
123+
return dx.norm() >= m_relTol;
127124
}
128125

129126
/// Return current value of the cost function

Framework/CurveFitting/src/FuncMinimizers/LevenbergMarquardtMDMinimizer.cpp

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
#include "MantidKernel/Logger.h"
1919

2020
#include <cmath>
21-
#include <gsl/gsl_blas.h>
2221

2322
namespace Mantid::CurveFitting::FuncMinimisers {
2423
namespace {
@@ -194,15 +193,11 @@ bool LevenbergMarquardtMDMinimizer::iterate(size_t /*iteration*/) {
194193
double dL;
195194
// der -> - der - 0.5 * hessian * dx
196195

197-
EigenMatrix tempHessianTr = m_costFunction->getHessian().tr();
198-
const gsl_matrix_const_view tempHessianTrGSL = getGSLMatrixView_const(tempHessianTr.inspector());
199-
const gsl_vector_const_view dxGSL = getGSLVectorView_const(dx.inspector());
200-
gsl_vector_view ddGSL = getGSLVectorView(dd.mutator());
201-
202-
gsl_blas_dgemv(CblasNoTrans, -0.5, &tempHessianTrGSL.matrix, &dxGSL.vector, 1., &ddGSL.vector);
196+
EigenVector Trdx = m_costFunction->getHessian().tr() * dx;
197+
Trdx *= -0.5;
198+
dd += Trdx;
203199
// calculate the linear part of the change in cost function
204-
// dL = - der * dx - 0.5 * dx * hessian * dx
205-
gsl_blas_ddot(&ddGSL.vector, &dxGSL.vector, &dL);
200+
dL = dd.dot(dx);
206201

207202
double F1 = m_costFunction->val();
208203
if (verbose) {
@@ -216,7 +211,7 @@ bool LevenbergMarquardtMDMinimizer::iterate(size_t /*iteration*/) {
216211
if (m_rho >= 0) {
217212
EigenVector p(n);
218213
m_costFunction->getParameters(p);
219-
double dx_norm = gsl_blas_dnrm2(&dxGSL.vector);
214+
double dx_norm = dx.norm();
220215
if (dx_norm < absError) {
221216
if (verbose) {
222217
g_log.warning() << "Successful fit, parameters changed by less than " << absError << '\n';

Framework/CurveFitting/src/Functions/ChebfunBase.cpp

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
#include "MantidCurveFitting/GSLFunctions.h"
1212
#include "MantidCurveFitting/HalfComplex.h"
1313

14-
#include <gsl/gsl_eigen.h>
15-
#include <gsl/gsl_errno.h>
1614
#include <gsl/gsl_fft_halfcomplex.h>
1715
#include <gsl/gsl_fft_real.h>
1816

17+
#include <Eigen/Eigenvalues>
18+
1919
#include <algorithm>
2020
#include <cassert>
2121
#include <cmath>
@@ -684,21 +684,16 @@ std::vector<double> ChebfunBase::roots(const std::vector<double> &a) const {
684684
C.set(N + i, lasti, tmp);
685685
}
686686

687-
gsl_vector_complex *eigenval = gsl_vector_complex_alloc(N2);
688-
auto workspace = gsl_eigen_nonsymm_alloc(N2);
689-
690-
EigenMatrix C_tr(C.tr());
691-
gsl_matrix_view C_tr_gsl = getGSLMatrixView(C_tr.mutator());
692-
gsl_eigen_nonsymm(&C_tr_gsl.matrix, eigenval, workspace);
693-
gsl_eigen_nonsymm_free(workspace);
687+
Eigen::EigenSolver<Eigen::MatrixXd> es(C.mutator());
688+
const Eigen::VectorXcd evals = es.eigenvalues();
694689

695690
const double Dx = endX() - startX();
696691
bool isFirst = true;
697692
double firstIm = 0;
698693
for (size_t i = 0; i < N2; ++i) {
699-
auto val = gsl_vector_complex_get(eigenval, i);
700-
double re = GSL_REAL(val);
701-
double im = GSL_IMAG(val);
694+
const std::complex<double> val = evals[static_cast<Eigen::Index>(i)];
695+
double re = val.real();
696+
double im = val.imag();
702697
double ab = re * re + im * im;
703698
if (fabs(ab - 1.0) > 1e-2) {
704699
isFirst = true;
@@ -715,8 +710,6 @@ std::vector<double> ChebfunBase::roots(const std::vector<double> &a) const {
715710
isFirst = true;
716711
}
717712
}
718-
gsl_vector_complex_free(eigenval);
719-
720713
return r;
721714
}
722715

@@ -824,25 +817,24 @@ std::vector<double> ChebfunBase::smooth(const std::vector<double> &xvalues, cons
824817
return y;
825818
}
826819

827-
// high frequency filter values: smooth decreasing function
828-
auto ri0f = static_cast<double>(i0 + 1);
829-
double xm = (1.0 + ri0f) / 2;
830-
ym /= ri0f;
831-
double a1 = (xy - ri0f * xm * ym) / (xx - ri0f * xm * xm);
832-
double b1 = ym - a1 * xm;
833-
834-
// std::cerr << "(a1,b1) = (" << a1 << ',' << b1 << ')' << '\n';
835-
836820
// calculate coeffs of a cubic c3*i^3 + c2*i^2 + c1*i + c0
837821
// which will replace the linear a1*i + b1 in building the
838822
// second part of the filter
839823
double c0, c1, c2, c3;
840824
{
825+
// high frequency filter values: smooth decreasing function
826+
auto ri0f = static_cast<double>(i0 + 1);
827+
double xm = (1.0 + ri0f) / 2;
828+
ym /= ri0f;
829+
841830
auto x0 = double(i0 + 1);
842831
auto x1 = double(n + 1);
843832
double sigma = g_tolerance / noise / 10;
844833
double s = sigma / (1.0 - sigma);
845834
double m1 = log(s);
835+
double a1 = (xy - ri0f * xm * ym) / (xx - ri0f * xm * xm);
836+
double b1 = ym - a1 * xm;
837+
// std::cerr << "(a1,b1) = (" << a1 << ',' << b1 << ')' << '\n';
846838
double m0 = a1 * x0 + b1;
847839
if (a1 < 0.0) {
848840
c3 = 0.0;

Framework/CurveFitting/test/CostFunctions/LeastSquaresTest.h

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
#include "MantidCurveFitting/Functions/UserFunction.h"
2323
#include "MantidCurveFitting/GSLFunctions.h"
2424

25-
#include <gsl/gsl_blas.h>
2625
#include <sstream>
2726

2827
using namespace Mantid;
@@ -201,15 +200,11 @@ class LeastSquaresTest : public CxxTest::TestSuite {
201200
dx.set(1, -0.2);
202201

203202
double L; // = d*dx + 0.5 * dx * H * dx
203+
EigenVector Trdx = H.tr() * dx;
204+
Trdx *= 0.5;
205+
g += Trdx;
206+
L = g.dot(dx);
204207

205-
EigenMatrix temp_H_tr = H.tr();
206-
const gsl_matrix_const_view temp_H_tr_gsl = getGSLMatrixView_const(temp_H_tr.inspector());
207-
const gsl_vector_const_view dx_gsl = getGSLVectorView_const(dx.inspector());
208-
gsl_vector_view g_gsl = getGSLVectorView(g.mutator());
209-
210-
gsl_blas_dgemv(CblasNoTrans, 0.5, &temp_H_tr_gsl.matrix, &dx_gsl.vector, 1., &g_gsl.vector);
211-
212-
gsl_blas_ddot(&g_gsl.vector, &dx_gsl.vector, &L);
213208
TS_ASSERT_DELTA(L, -0.145, 1e-10); // L + costFun->val() == 0
214209
}
215210

0 commit comments

Comments
 (0)