I tried to write a code in modern fortran (2018?) and one of my goals is to be able to convert subroutines like this one (old fortran 77) to the most recent fortran (2018?)
subroutine nobin(e,j,f)
common/binin/bini(40),nbn
er=e
do 100 i=j,40
er=er-bini(i)
if(er.le.0.0) go to 200
100 continue
f1=er/bini(40)
f=41.+f1-float(j)
if(j.gt.40) f=f1
go to 300
200 f=float(i-j)+er/bini(i)+1.0
300 continue
return
end
So far, I was able to write something like this (see below) but I was successful only for scalar variables. I am kind of stuck when I have to use arrays. In particular I should allocate and then give some values to the array Bini (the common block in the old fortran subroutine). And then to use these array in the function (find_bin_nobin) defined in the class. Any suggestion is appreciated as well as any formal improvement to the code. Many Thanks!
module precision
implicit none
public
integer, parameter :: pr = selected_real_kind(12,300)
integer, parameter :: ir = 4
end module precision
module constants
use precision
implicit none
public
real ( kind = pr ), parameter :: zero = 0.0_pr
real ( kind = pr ), parameter :: one = 1.0_pr
real ( kind = pr ), parameter :: four = 4.0_pr
real ( kind = pr ), parameter :: pi = four * atan(one)
end module constants
module class_Dntrnpr
use precision
use constants, only : zero, one
implicit none
type Find_bin
real ( kind = pr ) :: e
integer( kind = ir ) :: j
real ( kind = pr ), allocatable, dimension(:) :: bin
contains
procedure :: f => find_bin_nobin
end type Find_bin
contains
function find_bin_nobin(x, n) result(f)
class(Find_bin), intent(inout) :: x
integer( kind = ir ), intent(in) :: n
real ( kind = pr ) :: f, f1, er
integer( kind = ir ) :: i, k
allocate(x%bin(n))
er = x%e
i = x%j
k = i
er = er - x%bin(i)
do while ( er <= zero )
er = er - x%bin(i)
i = i + 1
enddo
f1 = er / x%bin(n)
f = ( n + 1 ) + f1 - dble(k)
if ( k > n ) then
f = f1; return
else
f = dble(i-k) + er / x%bin(i) + one
endif
return
end function find_bin_nobin
end module class_Dntrnpr
program test
use class_Dntrnpr
implicit none
integer( kind = ir ) :: nbmax,i
real ( kind = pr ), allocatable, dimension(:) :: bini
type(Find_bin) :: en
! type(Find_bin), allocatable, dimension(:) :: bin
en%e = 2.0
en%j = 1
nbmax = 40
allocate(bini(nbmax))
bini = 1.0
end program test
This looks like it might be more suited to code review, but I'm going to throw in my six cents anyway. Apologies in advance if I come off rude below; it's not my intent, I'm just trying to make suggestions as I see them, and to avoid cluttering advice with niceties.
module precision
selected_real_kind(15,307)is more standard than(12,300)for double precision. Maybe consider instead usingreal64fromiso_fortran_env.selected_int_kind(9)is more portable than4.So I would write this as:
module constants
zero = 0.0_prare generally bad practice. With a modern compiler they won't give you any speedup, and they reduce readability. Just use the actual numbers.real(pr)overreal (kind = pr). It conveys the same information, and is more concise.So I would write this as:
Class definition
eandj).Find_binto something more descriptive. Is this class a "find bin"?binrepresent a single bin, or should it bebins?f => find_bin_nobinshould really benobin => nobin_Find_bin.find_bin_nobindoes two different things: intilalisebinand calculatef. Ideally, OOP procedures should be separated intosubroutines which modify their arguments andfunctions which do not modify their arguments. In this case, initialisingbincan be done in a constructor.So I would write this as:
Class procedure(s)
find_bin_nobininto a constructor and a function to calculatef.x%bin. You allocate it withallocate(x%bin(n)), but then start reading from it without setting its values first.iis different to the old version.kis unnecessary and adds confusion.dble(foo)should bereal(foo, pr).;for two lines in one.if(j.gt.40)doesn't have anelse. The line200 f=float(i-j)+er/bini(i)+1.0is always skipped by the linego to 300.returnat the end of a procedure. That happens automatically.So I would write this as:
Main program
With the modules changed as above, I would write this as:
A general comment:
eanderwitherrorif that's what they represent.