|
| 1 | +module newton |
| 2 | + |
| 3 | +use, intrinsic :: iso_fortran_env, only: wp => real64, stderr => error_unit |
| 4 | + |
| 5 | +implicit none (type, external) |
| 6 | + |
| 7 | +private |
| 8 | +public :: wp, newtopts, newton_exact, objfun, objfun_deriv |
| 9 | + |
| 10 | +!> derived type for containing the options for the newton method procedure, by default |
| 11 | +! these work okay with the dipole to spherical conversion problem but can be adjusted |
| 12 | +! by the user for other applications |
| 13 | +type :: newtopts |
| 14 | + real(wp) :: derivtol=1e-18 |
| 15 | + integer :: maxit=100 |
| 16 | + real(wp) :: tol=1e-9 |
| 17 | + logical :: verbose=.false. |
| 18 | +end type newtopts |
| 19 | + |
| 20 | +!> these interfaced define the types of functions needed to run Newton iterations |
| 21 | +! need to match custom functions defined in using modules; these are defined so |
| 22 | +! that there are multiple objective functions (conforming to these patterns) that |
| 23 | +! can be used with Newton's method. These are abstract because there are multiple |
| 24 | +! objective functions (in principle) that can be used with Newton's method |
| 25 | +abstract interface |
| 26 | + real(wp) function objfun(x,parms) |
| 27 | + import wp |
| 28 | + real(wp), intent(in) :: x |
| 29 | + real(wp), dimension(:), intent(in) :: parms |
| 30 | + end function |
| 31 | + |
| 32 | + real(wp) function objfun_deriv(x,parms) |
| 33 | + import wp |
| 34 | + real(wp), intent(in) :: x |
| 35 | + real(wp), dimension(:), intent(in) :: parms |
| 36 | + end function |
| 37 | +end interface |
| 38 | + |
| 39 | +contains |
| 40 | + |
| 41 | +!> this implmements the exact Newton method for solving a nonlinear equation |
| 42 | +subroutine newton_exact(f,fprime,x0,parms,newtparms,root,it,converged) |
| 43 | +procedure(objfun), pointer, intent(in) :: f |
| 44 | +procedure(objfun_deriv), pointer, intent(in) :: fprime |
| 45 | +real(wp), intent(in) :: x0 ! starting point for newton iteration |
| 46 | +real(wp),dimension(:), intent(in) :: parms ! fixed parameters of the newton iteration, f,fprime must accommodate whatever size array is passed in |
| 47 | +type(newtopts), intent(in) :: newtparms ! options for the iteration that can be set by the user |
| 48 | +real(wp), intent(out) :: root |
| 49 | +integer, intent(out) :: it |
| 50 | +logical, intent(out) :: converged |
| 51 | + |
| 52 | +real(wp) :: fval,derivative |
| 53 | + |
| 54 | +! check starting point is not too close to inflection |
| 55 | +if (abs(fprime(x0,parms))<newtparms%derivtol) then |
| 56 | +write(stderr,*) 'Warning: starting near inflection point, please change initial guess!' |
| 57 | +it=0; converged=.false.; root=x0; |
| 58 | +return |
| 59 | +end if |
| 60 | + |
| 61 | +! Newton iteration main loop |
| 62 | +it=1; root=x0; fval=f(root,parms); converged=.false. |
| 63 | +do while (.not. converged .and. it <= newtparms%maxit) |
| 64 | +derivative=fprime(root,parms) |
| 65 | +if (abs(derivative)<newtparms%derivtol) then |
| 66 | + write(stderr,*) 'Warning: Encountered inflection point during iteration: ',it |
| 67 | + return |
| 68 | +else |
| 69 | + root=root-fval/derivative |
| 70 | + fval=f(root,parms) |
| 71 | + if (newtparms%verbose) then |
| 72 | + print*, ' Iteration ',it,'; root ',root,' fval ',fval,' derivative ',derivative |
| 73 | + end if |
| 74 | + it=it+1 |
| 75 | + converged=abs(fval)<newtparms%tol |
| 76 | +end if |
| 77 | +end do |
| 78 | +it=it-1 |
| 79 | + |
| 80 | +end subroutine newton_exact |
| 81 | + |
| 82 | +end module newton |
| 83 | + |
| 84 | + |
| 85 | +program main |
| 86 | + |
| 87 | +use newton, only: wp, newtopts, objfun, objfun_deriv, newton_exact |
| 88 | + |
| 89 | +implicit none |
| 90 | + |
| 91 | +real(wp) :: q,p,r |
| 92 | + |
| 93 | +real(wp), parameter :: Re=6371e3_wp ! Earth radius in meters |
| 94 | + |
| 95 | +type(newtopts) :: newtparms |
| 96 | +real(wp), dimension(2) :: parms |
| 97 | +real(wp) :: r0 |
| 98 | +procedure(objfun), pointer :: f |
| 99 | +procedure(objfun_deriv), pointer :: fprime |
| 100 | +integer :: maxrestart, maxr, r0step |
| 101 | +integer :: it,ir0 |
| 102 | +logical :: converged |
| 103 | + |
| 104 | +! Set parameters of the restart and Newton iterations |
| 105 | +maxrestart=400 |
| 106 | +maxr=100*Re |
| 107 | +r0step=0.25*Re |
| 108 | +newtparms%maxit=100 |
| 109 | +newtparms%derivtol=1e-18 |
| 110 | +newtparms%tol=1e-11 |
| 111 | +newtparms%verbose=.false. |
| 112 | +f=>rpoly |
| 113 | +fprime=>rpoly_deriv |
| 114 | +parms=[q,p] |
| 115 | + |
| 116 | +! Newton iterations with restarting (see parameters above for limits) until we get a satisfactory result |
| 117 | +r=0; converged=.false.; ir0=1; |
| 118 | +do while (.not. converged .and. ir0<maxrestart .and. (r<=0 .or. r>maxr)) |
| 119 | + r0=(ir0-1)*(r0step) ! change starting point in increments of 0.25 Re until we get a "good" answer |
| 120 | + call newton_exact(f,fprime,r0,parms,newtparms,r,it,converged) |
| 121 | + ir0=ir0+1 |
| 122 | +end do |
| 123 | + |
| 124 | +print '(a,2f20.10)', ' r, Re = ', r, Re |
| 125 | +print '(a,i0)', ' iterations = ', it |
| 126 | +print '(a,l1)', ' converged = ', converged |
| 127 | + |
| 128 | +contains |
| 129 | + !> objective function for newton iterations for solutions of roots for r |
| 130 | + function rpoly(x,parms) result(fval) |
| 131 | + real(wp), intent(in) :: x |
| 132 | + real(wp), dimension(:), intent(in) :: parms |
| 133 | + real(wp) :: fval |
| 134 | + |
| 135 | + real(wp) :: q,p |
| 136 | + |
| 137 | + q=parms(1); p=parms(2); |
| 138 | + fval=q**2*(x/Re)**4 + 1/p*(x/Re) - 1 |
| 139 | + end function rpoly |
| 140 | + |
| 141 | + |
| 142 | + !> derivative objective function for newton iterations for roots of r |
| 143 | + function rpoly_deriv(x,parms) result(fval_deriv) |
| 144 | + real(wp), intent(in) :: x |
| 145 | + real(wp), dimension(:), intent(in) :: parms |
| 146 | + real(wp) :: fval_deriv |
| 147 | + |
| 148 | + real(wp) :: q,p |
| 149 | + |
| 150 | + q=parms(1); p=parms(2); |
| 151 | + fval_deriv=4/Re*q**2*(x/Re)**3 + 1/p/Re |
| 152 | + end function rpoly_deriv |
| 153 | + |
| 154 | + |
| 155 | +end program |
0 commit comments