Version: SMASH-3.4
rootsolver.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2023,2025
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #ifndef SRC_INCLUDE_SMASH_ROOTSOLVER_H_
11 #define SRC_INCLUDE_SMASH_ROOTSOLVER_H_
12 
13 #include <functional>
14 #include <memory>
15 #include <optional>
16 #include <string>
17 
18 #include "gsl/gsl_errno.h"
19 #include "gsl/gsl_math.h"
20 #include "gsl/gsl_roots.h"
21 
22 #include "logging.h"
23 
24 namespace smash {
25 static constexpr int LRootSolver = LogArea::RootSolver::id;
26 /**
27  * A class used for calculating the root of a one-dimensional equation.
28  * It takes care of all technicalities and provides an interface to GSL.
29  */
30 class RootSolver1D {
31  public:
32  /**
33  * Construct a new Root Solver 1D object
34  *
35  * \param[in] eq The function of which a root is desired
36  */
37  explicit RootSolver1D(std::function<double(double)> eq) {
38  root_eq_ = std::make_unique<std::function<double(double)>>(eq);
39  }
40 
41  ~RootSolver1D() { root_eq_ = nullptr; }
42 
43  /**
44  * Attempt to find a root in a given interval
45  *
46  * \param[in] initial_guess_low Lower boundary of the interval
47  * \param[in] initial_guess_high Higher boundary of the interval
48  * \param[in] itermax maximum number of steps for root finding
49  * \return the root if it was found
50  */
51  std::optional<double> try_find_root(double initial_guess_low,
52  double initial_guess_high,
53  size_t itermax) {
54  // check if root is in the given interval
55  if ((*root_eq_)(initial_guess_low) * (*root_eq_)(initial_guess_high) > 0) {
56  logg[LRootSolver].trace()
57  << "Function has same sign at both ends of the interval ["
58  << initial_guess_low << ", " << initial_guess_high
59  << "]. Root can't be found in this interval.";
60  return std::nullopt;
61  }
62  gsl_function function_GSL = {&(gsl_func), nullptr};
63  int status = GSL_CONTINUE;
64  size_t iter = 0;
65  Root_finder_ = gsl_root_fsolver_alloc(Solver_name_);
66  gsl_root_fsolver_set(Root_finder_, &function_GSL, initial_guess_low,
67  initial_guess_high);
68  do {
69  iter++;
70  status = gsl_root_fsolver_iterate(Root_finder_);
71  if (status != GSL_SUCCESS) {
72  logg[LRootSolver].debug("GSL ERROR in root finding: " +
73  static_cast<std::string>(gsl_strerror(status)));
74  break;
75  }
76  double xlow = gsl_root_fsolver_x_lower(Root_finder_);
77  double xhigh = gsl_root_fsolver_x_upper(Root_finder_);
78  status = gsl_root_test_interval(xlow, xhigh, 0, solution_precision_);
79  if (status == GSL_SUCCESS) {
80  double root = 0.5 * (xlow + xhigh);
81  gsl_root_fsolver_free(Root_finder_);
82  return root;
83  }
84  } while (status == GSL_CONTINUE && iter < itermax);
85  gsl_root_fsolver_free(Root_finder_);
86  return std::nullopt;
87  }
88 
89  private:
90  /// GSL solver to use for root finding
91  const gsl_root_fsolver_type *Solver_name_ = gsl_root_fsolver_brent;
92 
93  /// GSL root finding object to take care of root finding
94  gsl_root_fsolver *Root_finder_ = nullptr;
95 
96  /** Static pointer to the function to solve
97  * \note This member has to be \c static since it is used inside the `static
98  * gsl_func` method. As \c static methods exist and can be used without a
99  * class instance, it is not possible to use non-static members inside them
100  * (non-static members \b are bound to a class instance). \see gsl_func to
101  * understand why that method needs to be \c static as well.
102  */
103  static inline std::unique_ptr<std::function<double(double)>> root_eq_ =
104  nullptr;
105 
106  /// Expected precision of the root
107  double solution_precision_ = 1e-7;
108 
109  /**
110  * The function of which a root should be found in the form that GSL expects
111  *
112  * \param[in] x Argument of the function
113  *
114  * \return The value of the function for the given argument x
115  *
116  * \note
117  * This function needs to be \c static because GSL uses an object of type \c
118  * gsl_function that has to be initialised with a pointer to a function and
119  * this cannot be created from a class non-static method as a class non-static
120  * method cannot exist as entity without an instance of that type. On the
121  * contrary, a pointer to a static member function can be used as a normal
122  * pointer to a free function, because a \c static method exists without
123  * needing any instance of the class.
124  */
125  static double gsl_func(const double x, void *) { return (*root_eq_)(x); }
126 };
127 
128 } // namespace smash
129 
130 #endif // SRC_INCLUDE_SMASH_ROOTSOLVER_H_
A class used for calculating the root of a one-dimensional equation.
Definition: rootsolver.h:30
RootSolver1D(std::function< double(double)> eq)
Construct a new Root Solver 1D object.
Definition: rootsolver.h:37
double solution_precision_
Expected precision of the root.
Definition: rootsolver.h:107
gsl_root_fsolver * Root_finder_
GSL root finding object to take care of root finding.
Definition: rootsolver.h:94
const gsl_root_fsolver_type * Solver_name_
GSL solver to use for root finding.
Definition: rootsolver.h:91
static double gsl_func(const double x, void *)
The function of which a root should be found in the form that GSL expects.
Definition: rootsolver.h:125
std::optional< double > try_find_root(double initial_guess_low, double initial_guess_high, size_t itermax)
Attempt to find a root in a given interval.
Definition: rootsolver.h:51
static std::unique_ptr< std::function< double(double)> > root_eq_
Static pointer to the function to solve.
Definition: rootsolver.h:103
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > & logg
An array that stores all pre-configured Logger objects.
Definition: logging.h:245
Definition: action.h:24
static constexpr int LRootSolver
Definition: rootsolver.h:25