|
| 1 | +module types |
| 2 | +implicit none |
| 3 | +private |
| 4 | +public dp |
| 5 | + |
| 6 | +integer, parameter :: dp=kind(0d0) |
| 7 | + |
| 8 | +end module |
| 9 | + |
| 10 | +module find_fit_module |
| 11 | + |
| 12 | +! This module contains a general function find_fit() for a nonlinear least |
| 13 | +! squares fitting. The function can fit any nonlinear expression to any data. |
| 14 | + |
| 15 | +use minpack, only: lmdif1 |
| 16 | +use types, only: dp |
| 17 | +implicit none |
| 18 | +private |
| 19 | +public find_fit |
| 20 | + |
| 21 | +contains |
| 22 | + |
| 23 | +subroutine find_fit(data_x, data_y, expr, pars) |
| 24 | +! Fits the (data_x, data_y) arrays with the function expr(x, pars). |
| 25 | +! The user can provide any nonlinear function 'expr' depending on any number of |
| 26 | +! parameters 'pars' and it must return the evaluated expression on the |
| 27 | +! array 'x'. The arrays 'data_x' and 'data_y' must have the same |
| 28 | +! length. |
| 29 | +real(dp), intent(in) :: data_x(:), data_y(:) |
| 30 | +interface |
| 31 | + function expr(x, pars) result(y) |
| 32 | + use types, only: dp |
| 33 | + implicit none |
| 34 | + real(dp), intent(in) :: x(:), pars(:) |
| 35 | + real(dp) :: y(size(x)) |
| 36 | + end function |
| 37 | +end interface |
| 38 | +real(dp), intent(inout) :: pars(:) |
| 39 | + |
| 40 | +real(dp) :: tol, fvec(size(data_x)) |
| 41 | +integer :: iwa(size(pars)), info, m, n |
| 42 | +real(dp), allocatable :: wa(:) |
| 43 | + |
| 44 | +tol = sqrt(epsilon(1._dp)) |
| 45 | +m = size(fvec) |
| 46 | +n = size(pars) |
| 47 | +allocate(wa(m*n + 5*n + m)) |
| 48 | +call lmdif1(fcn, m, n, pars, fvec, tol, info, iwa, wa, size(wa)) |
| 49 | +if (info /= 1) stop "failed to converge" |
| 50 | + |
| 51 | +contains |
| 52 | + |
| 53 | +subroutine fcn(m, n, x, fvec, iflag) |
| 54 | +integer, intent(in) :: m, n, iflag |
| 55 | +real(dp), intent(in) :: x(n) |
| 56 | +real(dp), intent(out) :: fvec(m) |
| 57 | +! Suppress compiler warning: |
| 58 | +fvec(1) = iflag |
| 59 | +fvec = data_y - expr(data_x, x) |
| 60 | +end subroutine |
| 61 | + |
| 62 | +end subroutine |
| 63 | + |
| 64 | +end module |
| 65 | + |
| 66 | + |
| 67 | +program example_primes |
| 68 | + |
| 69 | +! Find a nonlinear fit of the form a*x*log(b + c*x) to a list of primes. |
| 70 | + |
| 71 | +use find_fit_module, only: find_fit |
| 72 | +use types, only: dp |
| 73 | +implicit none |
| 74 | + |
| 75 | +real(dp) :: pars(3) |
| 76 | +real(dp), parameter :: y(*) = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, & |
| 77 | + 37, 41, 43, 47, 53, 59, 61, 67, 71] |
| 78 | +integer :: i |
| 79 | +pars = [1._dp, 1._dp, 1._dp] |
| 80 | +call find_fit([(real(i, dp), i=1,size(y))], y, expression, pars) |
| 81 | +print *, pars |
| 82 | + |
| 83 | +contains |
| 84 | + |
| 85 | +function expression(x, pars) result(y) |
| 86 | +real(dp), intent(in) :: x(:), pars(:) |
| 87 | +real(dp) :: y(size(x)) |
| 88 | +real(dp) :: a, b, c |
| 89 | +a = pars(1) |
| 90 | +b = pars(2) |
| 91 | +c = pars(3) |
| 92 | +y = a*x*log(b + c*x) |
| 93 | +end function |
| 94 | + |
| 95 | +end program |
0 commit comments