Files
geant4/source/global/HEPNumerics/include/G4FunctionSolver.hh
2025-12-05 08:54:02 +01:00

151 lines
4.9 KiB
C++

//
// ********************************************************************
// * License and Disclaimer *
// * *
// * The Geant4 software is copyright of the Copyright Holders of *
// * the Geant4 Collaboration. It is provided under the terms and *
// * conditions of the Geant4 Software License, included in the file *
// * LICENSE and available at http://cern.ch/geant4/license . These *
// * include a list of copyright holders. *
// * *
// * Neither the authors of this software system, nor their employing *
// * institutes,nor the agencies providing financial support for this *
// * work make any representation or warranty, express or implied, *
// * regarding this software system or assume any liability for its *
// * use. Please see the license in the file LICENSE and URL above *
// * for the full disclaimer and the limitation of liability. *
// * *
// * This code implementation is the result of the scientific and *
// * technical work of the GEANT4 collaboration. *
// * By using, copying, modifying or distributing the software (or *
// * any work based on the software) you agree to acknowledge its *
// * use in resulting scientific publications, and indicate your *
// * acceptance of all terms of the Geant4 Software license. *
// ********************************************************************
//
// G4FunctionSolver
//
// Class description:
//
// Templated utility class to solve equation F(x) = 0 on interval [a, b]
// Parameters of the class: tolerance - relative accuracy of the method,
// maxIter - max number of iterations. User should provide a class with
// only one method: T_Function::Function(G4double x). The main method
// of the class is 'G4bool G4FunctionSolver::FindRoot(G4double& x)', where
// user defines initial value x, which is modifided to be a final solution
// of the equation.
//
// If the equation cannot be resolved with required accuracy FindRoot()
// method returns "false".
//
// Parameters of the solver may be changed before the new call via Set
// methods.
//
// Created 04.10.2025 V.Ivanchenko
//
#ifndef G4FunctionSolver_h
#define G4FunctionSolver_h 1
#include "globals.hh"
template <class T_Function> class G4FunctionSolver
{
public:
G4FunctionSolver(T_Function* ff, const G4int iterations, const G4double tol)
: maxIter(iterations), tolerance(tol), tF(ff) {};
// copy constructor
G4FunctionSolver(const G4FunctionSolver& right) = delete;
// destructor
~G4FunctionSolver() = default;
// operators
G4FunctionSolver& operator=(const G4FunctionSolver& right) = delete;
G4bool operator==(const G4FunctionSolver& right) const = delete;
G4bool operator!=(const G4FunctionSolver& right) const = delete;
inline void SetMaxIterations(const G4int iterations)
{ maxIter = iterations; }
inline void SetTolerance(const G4double epsilon)
{ tolerance = epsilon; }
inline void SetIntervalLimits(const G4double Limit1, const G4double Limit2)
{
aa = std::min(Limit1, Limit2);
bb = std::max(Limit1, Limit2);
}
// Calculates the root of the equation Function(x)=0
inline G4bool FindRoot(G4double& x)
{
G4double a = aa;
G4double b = bb;
// check the interval before the start
x = std::min(std::max(x, a), b);
// check initial function
G4double fc = tF->Function(x);
if (0.0 == fc) { return true; }
// define accuracy in X
G4double epsX = tolerance*x;
// define accuracy in Y
G4double epsY = tolerance*fc;
// the interval is too small
if (std::abs(a - b) <= epsX)
{
x = 0.5*(a + b);
return true;
}
// check edges
G4double fa = tF->Function(a);
G4double fb = tF->Function(b);
// root should be inside interval
if (fa*fb >= 0.0)
{
x = (std::abs(fa) <= std::abs(fb)) ? a : b;
return (std::min(std::abs(fa), std::abs(fb)) < epsY);
}
// fa*fb < 0.0 - finding the root by iterative procedure,
// the loop is completed if function is below epsY
// or if x become close to edges with accuracy epsX
for (G4int i = 0; i < maxIter; ++i)
{
x = (a*fb - b*fa)/(fb - fa);
fc = tF->Function(x);
if (std::abs(fc) < epsY) { return true; }
G4double delta = std::min((x - a), (b - x));
if (delta < epsX) { return true; }
else if (fa*fc < 0.0) { b = x; fb = fc; }
else { a = x; fa = fc; }
}
// number of iterations exceed the limit
return false;
}
private:
// maximum number of iterations
G4int maxIter;
// relative accuracy in X and Y
G4double tolerance;
// interval limits [a,b]
G4double aa{0.0};
G4double bb{0.0};
T_Function* tF;
};
#endif