How to improve speed of MPI I/O on large number of cores? - fortran90

I've been trying to run a code using MPI I/O on a large number of cores. The time required for each core to read from and write to a single file (the same for all cores) increases with the number of cores used. I'm currently using 512 cores and this problem is making my project unfeasible. The problem appears, however, even when running on 8 cores; it then takes about 0.2 seconds to read the first real number in the file. On 32 cores it takes more then 30 seconds to write one real number. I'm running it here: https://www.msi.umn.edu/hpc/itasca. The following simple code generates exactly this problem (the counting of the number of elements in the file might seem unnecessary here but it is necessary in my actual code):
PROGRAM MAIN
USE MPI
IMPLICIT NONE
! INITIALIZING VARIABLES
REAL(8) :: A, B
INTEGER :: COUNT_IO, i, j, ST, GO, tag, t, nb_bytes, N, d_each, d_start, d_end, NN
REAL(8) :: time_start, time_end
! VARIABLES RELATED TO MPI
INTEGER :: ierror ! returns error messages from the mpi subroutines
INTEGER :: rank ! identification number of each processor
INTEGER :: nproc ! number of processors
INTEGER, DIMENSION(mpi_status_size):: status
INTEGER(kind= MPI_OFFSET_KIND ) :: offset
INTEGER :: fh ! file handle
! EXECUTABLE
! INITIALIZE THE MPI ENVIRONMENT
CALL MPI_INIT(ierror) ! initialize MPI
CALL MPI_COMM_RANK(MPI_COMM_WORLD,rank,ierror) ! obtain rank for each node
CALL MPI_COMM_SIZE(MPI_COMM_WORLD,nproc,ierror) ! obtain the number of nodes
CALL MPI_TYPE_SIZE(MPI_REAL8,nb_bytes,ierror)
CALL MPI_FILE_OPEN (MPI_COMM_WORLD,"file.dat",MPI_MODE_RDWR+MPI_MODE_UNIQUE_OPEN,MPI_INFO_NULL,fh,ierror)
NN = 2048
DO d_each=1,NN
IF (d_each*nproc>=NN) EXIT
END DO
d_start = rank*d_each+1
d_end = MIN((rank+1)*d_each,NN)
DO t = d_start,d_end
! READING ONE THREAD AT A TIME
tag = 1
GO = 0
IF (rank .gt. 0) THEN
CALL MPI_RECV (GO,1,MPI_INTEGER,rank-1,tag, MPI_COMM_WORLD ,status,ierror)
ENDIF
time_start = MPI_WTIME()
i = 0
ST = 0
COUNT_IO = 0
DO WHILE ((i .lt. 100000) .AND. (ST .eq. 0))
i = i+1
offset = nb_bytes*(i-1)
CALL MPI_FILE_READ_AT (fh,offset,A,1,MPI_REAL8,status,ierror)
IF (status(1) .eq. 0) THEN
COUNT_IO = i
ST = 1
ELSE
COUNT_IO = 0
END IF
ENDDO
N = (COUNT_IO - 1)
IF (N .gt. 0) THEN
offset = 0
CALL MPI_FILE_READ_AT (fh,offset,B,1,MPI_REAL8,status,ierror)
ENDIF
time_end = MPI_WTIME()
PRINT *, 'My rank is', rank, 'Time for read =',time_end-time_start
GO = 1
IF (rank .lt. nproc-1) THEN
CALL MPI_SEND (GO,1, MPI_INTEGER ,rank+1,tag, MPI_COMM_WORLD ,ierror)
ENDIF
CALL MPI_BARRIER(MPI_COMM_WORLD,ierror)
! WRITING ONE THREAD AT A TIME
tag = 2
GO = 0
IF (rank .gt. 0) THEN
CALL MPI_RECV (GO,1,MPI_INTEGER,rank-1,tag, MPI_COMM_WORLD ,status,ierror)
ENDIF
time_start = MPI_WTIME()
i = 0
ST = 0
COUNT_IO = 0
DO WHILE ((i .lt. 100000) .AND. (ST .eq. 0))
i = i+1
offset = nb_bytes*(i-1)
CALL MPI_FILE_READ_AT (fh,offset,A,1,MPI_REAL8,status,ierror)
IF (status(1) .eq. 0) THEN
COUNT_IO = i
ST = 1
ELSE
COUNT_IO = 0
END IF
ENDDO
N = (COUNT_IO - 1)
offset = nb_bytes*N
CALL MPI_FILE_WRITE_AT (fh,offset,0.0D0,1,MPI_REAL8,status,ierror)
time_end = MPI_WTIME()
PRINT *, 'My rank is', rank, 'Time for write =',time_end-time_start
GO = 1
IF (rank .lt. nproc-1) THEN
CALL MPI_SEND (GO,1, MPI_INTEGER ,rank+1,tag, MPI_COMM_WORLD ,ierror)
ENDIF
CALL MPI_BARRIER(MPI_COMM_WORLD,ierror)
ENDDO
CALL MPI_FILE_CLOSE (fh,ierror)
CALL MPI_FINALIZE(ierror)
END PROGRAM MAIN

The main thing to realize here is that you can read in the data in one fell swoop (or, if memory is a problem, in chunks - but it can be in much larger chunks than individual doubles!) and that you don't need to skip to the end of the file one double at a time.
Here's an example which will read in the data in arbitrary chunk sizes, processes the data as you will, and appends some data (in this case, everyone just adds 4 copies of their rank to the end of the file). For simplicity, little python scripts help with writing and displaying test data.
$ ./writedata.py
$ ./readdata.py
[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14.
15. 16. 17. 18. 19. 20. 21. 22. 23. 24.]
$ mpirun -np 3 ./usepario
rank: 0 got data: 0.000... 24.000
rank: 1 got data: 0.000... 24.000
rank: 2 got data: 0.000... 24.000
$ ./readdata.py
[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14.
15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 0. 0. 0. 0. 1.
1. 1. 1. 2. 2. 2. 2.]
usepario.f90:
module pario
contains
function openFile(filename)
use mpi
implicit none
integer :: openFile, ierr
character(len=*) :: filename
integer(MPI_OFFSET_KIND) :: off = 0
call MPI_File_open(MPI_COMM_WORLD, filename, &
ior(MPI_MODE_RDWR, MPI_MODE_UNIQUE_OPEN), &
MPI_INFO_NULL, openFile, ierr)
call MPI_File_set_view(openFile, off, &
MPI_DOUBLE_PRECISION, MPI_DOUBLE_PRECISION, &
"native", MPI_INFO_NULL, ierr)
end function openFile
subroutine closeFile(fh)
use mpi
implicit none
integer :: fh, ierr
call MPI_File_close(fh, ierr)
end subroutine closeFile
function filesizedoubles(fh)
use mpi
implicit none
integer :: fh, ierr
integer(MPI_OFFSET_KIND) :: filesize, filesizedoubles
integer :: dblsize
call MPI_File_get_size(fh, filesize, ierr)
call MPI_type_size(MPI_DOUBLE_PRECISION, dblsize, ierr)
filesizedoubles = filesize / dblsize
end function filesizedoubles
subroutine getdatablock(fh, blocksize, datablock, datasize)
use mpi
implicit none
integer :: fh, ierr
integer :: blocksize, datasize
double precision, dimension(:) :: datablock
integer(MPI_OFFSET_KIND) :: fileloc
integer, dimension(MPI_STATUS_SIZE) :: rstatus
! you can also experiment with read_all for non collective/synchronous file
! access
call MPI_File_read(fh, datablock, blocksize, MPI_DOUBLE_PRECISION, &
rstatus, ierr)
call MPI_Get_count(rstatus, MPI_DOUBLE_PRECISION, datasize, ierr)
end subroutine getdatablock
subroutine eachappend(fh, filesize, numitems, newdata)
use mpi
implicit none
integer :: fh, numitems
integer(MPI_OFFSET_KIND) :: filesize
double precision, dimension(:) :: newdata
integer :: rank, ierr
integer(MPI_OFFSET_KIND) :: offset
call MPI_Comm_rank(MPI_COMM_WORLD, rank, ierr)
offset = filesize + rank*numitems
call MPI_File_write_at_all(fh, offset, newdata, numitems, &
MPI_DOUBLE_PRECISION, &
MPI_STATUS_IGNORE, ierr)
end subroutine eachappend
end module pario
program usepario
use mpi
use pario
implicit none
integer :: fileh
integer, parameter :: bufsize=1000, newsize=4
integer(MPI_OFFSET_KIND) :: filesize
double precision, allocatable, dimension(:) :: curdata, newdata
integer :: datasize
integer :: rank, ierr
call MPI_Init(ierr)
call MPI_Comm_rank(MPI_COMM_WORLD, rank, ierr)
allocate(curdata(bufsize))
fileh = openFile("data.dat")
filesize = filesizedoubles(fileh)
do
call getdatablock(fileh, bufsize, curdata, datasize)
!!
!! process data here
!!
!! do i=1,datasize
!! ...dostuff...
!! end do
!!
print '(1X,A,I3,A,F8.3,A,F8.3)', 'rank: ', rank, ' got data: ', curdata(1), '...', curdata(datasize)
if (datasize /= bufsize) exit
end do
deallocate(curdata)
allocate(newdata(newsize))
newdata = rank
call eachappend(fileh, filesize, newsize, newdata)
call closeFile(fileh)
call MPI_Finalize(ierr)
end program usepario
writedata.py:
#!/usr/bin/env python
import numpy
numdoubles = 25
data = numpy.arange(numdoubles,dtype=numpy.float64)
data.tofile("data.dat")
readdata.py:
#!/usr/bin/env python
import numpy
data = numpy.fromfile("data.dat",dtype=numpy.float64)
print data

Related

Julia: Inexact error when trying to get the integer part of a BigFloat

I am interested in getting the digits of a BigFloat in the form of bytes. I get a very strange error that I cannot debug. I provide a minimal example where the error appears.
function floatToBytes(x::BigFloat)
ret = zeros(UInt8, 4)
xs = significand(x)/2
b = UInt8(0)
for i = 1:4
xs *= 256
b = trunc(UInt8, xs)
ret[i] = b
xs -= b
end
return ret
end
println( floatToBytes(BigFloat(0.9921875001164153)) )
println( floatToBytes(BigFloat(0.9960937501164153)) )
What I get when running this is
UInt8[0xfe, 0x00, 0x00, 0x00]
ERROR: LoadError: InexactError()
Stacktrace:
[1] trunc(::Type{UInt8}, ::BigFloat) at ./mpfr.jl:201
etc.
It seems that it doesn't want to turn 255 into a UInt8. I can circumvent the problem by defining the function as
function floatToBytes(x::BigFloat)
ret = zeros(UInt8, 4)
xs = significand(x)/2
b = UInt8(0)
for i = 1:4
xs *= 256
try
b = trunc(UInt8, xs)
catch
b = trunc(UInt8, xs-1)+UInt8(1)
end
ret[i] = b
xs -= b
end
return ret
end
But this is highly unsatisfactory. What is going on here?
The problem looks like a bug in trunc for BigFloat. The problem is the current code does (typemin(T) <= x <= typemax(T)) || throw(InexactError(:trunc, T, x)) which throws an error because x is larger than 255 which is the typemax.
It actually needs to do the trunc in BigFloat domain and then cast to T (and have the cast check for typemax).
I've opened an issue regarding this at: https://github.com/JuliaLang/julia/issues/24041
In the meantime, a solution could be to do:
UInt8(trunc(xs))
i.e. trunc first and cast later. For example:
julia> UInt8(trunc(BigFloat(0.9960937501164153)*256))
0xff

Cryptic TypeError: 'decimal.Decimal' object cannot be interpreted as an integer

I am struggling to understand why this function apparently fails in the Jupyter Notebook, but not in the IPython shell:
def present_value( r, n, fv = None, pmt = None ):
'''
Function to compute the Present Value based on interest rate and
a given future value.
Arguments accepted
------------------
* r = interest rate,
which should be given in its original percentage, eg.
5% instead of 0.05
* n = number of periods for which the cash flow,
either as annuity or single flow from one present value
* fv = future value in dollars,
if problem is annuity based, leave this empty
* pmt = each annuity payment in dollars,
if problem is single cash flow based, leave this empty
'''
original_args = [r, n, fv, pmt]
dec_args = [Decimal( arg ) if arg != None
else arg
for arg in original_args
]
if dec_args[3] == None:
return dec_args[2] / ( ( 1 + ( dec_args[0] / 100 ) )**dec_args[1] )
elif dec_args[2] == None:
# annuity_length = range( 1, dec_args[1] + 1 )
# Not allowed to add a Decimal object
# with an integer and to use it
# in the range() function,
# so we dereference the integer from original_args
annuity_length = range( 1, original_args[1] + 1 )
# Apply discounting to each annuity payment made
# according to number of years left till end
all_compounded_pmt = [dec_args[3] * ( 1 / ( ( 1 + dec_args[0] / 100 ) ** time_left ) ) \
for time_left in annuity_length
]
return sum( all_compounded_pmt )
When I imported the module that this function resides in, named functions.py, using from functions import *, and then executed present_value(r=7, n=35, pmt = 11000), I got the error:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-93-c1cc587f7e27> in <module>()
----> 1 present_value(r=7, n=35, pmt = 11000)
/path_to_file/functions.py in present_value(r, n, fv, pmt)
73 if dec_args[3] == None:
74 return dec_args[2]/((1 + (dec_args[0]/100))**dec_args[1])
---> 75
76 elif dec_args[2] == None:
77 # annuity_length = range(1, dec_args[1]+1)
TypeError: 'decimal.Decimal' object cannot be interpreted as an integer
but in the IPython shell, evaluating this function it works perfectly fine:
In [42]: functions.present_value(r=7, n=35, pmt = 11000)
Out[42]: Decimal('142424.39530474029537')
Can anyone please help me with this really confusing and obscure issue?

Reading the Fortran real data type into Matlab from a hex dump

As the title says I am writing a large amount of real arrays from Fortran into an unformatted file, and then trying to read that binary file into Matlab. I have successfully made my script work for strings and integers, but It does not correctly read my real numbers from the hex dump.
As a test case I was using the number 5.49. Interesting side note, according to an online converter that is 40 af ae 14, yet when I check my hexfile that portion of my code is reading 14 ae af 40. I have tried reading it in as a float32 and double, and I have changed fro neutral to little endian to big endian. Any ideas?
Here is a simple example of my code:
First the Fortran write statements
REAL :: floating = 5.49
open(unit = 2, file = "anxietySource", form = "unformatted", status = "new", action = "readwrite")
write(unit = 2 ) floating
Now the Matlab read statement
fid = fopen('anxietySource', 'rb');
h1 = fread(fid, 1, 'int32'); %this is just reading off the starter bits that tell me how long my write statement is
floating = fread(fid,1,'float32');
display(floating);
fclose(fid);
My guess is that there is something funky with the Fortran REAL type. Maybe it's not quite a floating point?
Good job on the bit level work getting this far, you are almost there.
"Big Endien versus little..."
https://gcc.gnu.org/onlinedocs/gfortran/CONVERT-specifier.html
http://www.lahey.com/docs/lfenthelp/F95ARINQUIREStmt.htm
https://software.intel.com/en-us/forums/intel-fortran-compiler-for-linux-and-mac-os-x/topic/270026
There is also manually shifting it https://www.nsc.liu.se/~boein/f77to90/a5.html#section10
MODULE Shifty
PRIVATE
INTERFACE Shift
MODULE PROCEDURE Shift4 !,Shift8
END INTERFACE
CONTAINS
REAL FUNCTION Shift4(MyFloat)
IMPLICIT NONE
REAL, INTENT(IN) :: MyFloat
INTEGER*4 :: Float1
INTEGER*4 :: Float2
INTEGER*4 :: Float3
INTEGER*4 :: Float4
INTEGER*4 :: MyInt
EQUIVALENCE(MyInt, MyFloat)
INTEGER*4 :: MyIntOut
EQUIVALENCE(MyIntOut, Shift4)
WRITE(*,20) MyInt
20 FORMAT('Incoming Real=',1PE12.5,' ==",Z8.8,'"')
Float1 = IBITS(MyInt, 0, 8)
Float2 = IBITS(MyInt, 8, 8)
Float3 = IBITS(MyInt,16, 8)
Float4 = IBITS(MyInt,24, 8)
WRITE(*,30) 1, Float1 !Check
WRITE(*,30) 2, Float2
WRITE(*,30) 3, Float3
WRITE(*,30) 4, Float4
30 FORMAT('Float',I1,'="',Z2.2,'"')
Float1 = ISHFT(Float1, 24)
Float2 = ISHFT(Float2, 16)
Float3 = ISHFT(Float3, 8)
Float4 = ISHFT(Float4, 0)
MyIntOut = IOR(Float1 , Float2)
MyIntOut = IOR(MyIntOut, Float3)
MyIntOut = IOR(MyIntOut, Float4)
WRITE(*,20) MyInt, MyIntOut
20 FORMAT('Incoming Real="',Z8.8,' Outgoing="',Z8.8,'"')
RETURN
END FUNCTION Shift4
END MODULE Shifty
PROGRAM MAT2F90
USE Shifty
IMPLICIT NONE
TYPE MATLAB_HEADER
CHARACTER(LEN=120) :: Descriptor !should be 116 and INT*8 !!
INTEGER*4 :: Offset
INTEGER*2 :: Version
CHARACTER(LEN=2) :: Endien
END TYPE MATLAB_HEADER
TYPE MATLAB_SUBHEADER
INTEGER*4 :: Type
INTEGER*4 :: Bytes
END TYPE MATLAB_SUB_HEADER
TYPE(MATLAB_HEADER) :: Head
TYPE(MATLAB_SUB_HEADER),DIMENSION(20) :: Tag
CHARACTER(LEN=12), DIMENSION(18) :: Matlab_Version = RESHAPE(&
['miINT8 ','miUINT8 ','miINT16 ','miUINT16 ', &
'miINT32 ','miUINT32 ','miSINGLE ','Reserved ', &
'miDOUBLE ','Reserved ','Reserved ','miINT64 ', &
'miUINT64 ','miMATRIX ','miCOMPRESSED','miUTF8 ', &
'miUTF16 ','miUTF32 '],[18])
LOGICAL :: Swap
...
OPEN(UNIT=22,FILE='<somename>',ACCESS='STREAM',FORM='UNFORMATTED',IOSTAT=Status)
IF(Status =/0 ) ...Do something
READ(20,IOSTAT=Status) Head
IF(Status =/0 ) ...Do something
WRITE(*,*)'Head.Descriptor="',Head.Descriptor,'"'
WRITE(*,*)'Head.Offset = ',Head.Offset
WRITE(*,*)'Head.Version = ',Head.Version
WRITE(*,*)'Head.Endien ="',Head.Endian,'"'
IF(Head.Endian == 'IM') THEN
SWAP = .FALSE.
ELSEIF(Head.Endian == 'MI') THEN
SWAP = .TRUE.
ELSE
WRITE(*,*)'Unknown Endien="',Head.Endian,'"'
STOP
ENDIF
READ(20,IOSTAT=Status) Tag(1)
IF(Status =/0 ) ...Do something
WRITE(*,*)'Tag(1).Type = ',Tag(1).Type,' == "',Matlab_Version(Tag(1).Type),'"'
WRITE(*,*)'Tag(1).Bytes= ',Tag(1).Bytes
!read and swap if need be...
!There is padding to an 8type boundary
!Read the next tag and data... etc
Section 1-5 to 1-9 https://data.cresis.ku.edu/data/mat_reader/matfile_format.pdf . You may notice like I did that type15 is 'miCOMPRESSED', so at that point one needs the uncompressor to make sense of it.
You'll need to test it like you did before, because it is easy for me to get the order wrong, and I am doing this by memory.(Updated as I was doing this today, but in a subroutine, so it should work as a function??)
I made it 4 in case you need an '8' version... And then you just call Shifty() and you can get the one that matches your data.

Imprecise Assigned value

In a simple program I want to build a matrix by defining a function. The problem is that a variable declared and initialized in the program has the exact assinged value of zero (zero_test)and some components of the matrix(D(4,1), D(1,4) etc.) which are assigned to 0., are not exactly zero. The latter have a value of order E-308 or E-291etc. I wonder why is there a difference.
Due to some articles I read, like this, the other components(D(1,1), D(1,2) etc.) are imprecise because of the transformation to the binary format.
System info: IA-32, Intel Visual Fortran 11.1.048 integrated with Microsoft Visual Studio 2008
The code:
program test
implicit none
real(8),parameter :: LAMBDA=75.e9,MU=50.e9
integer,parameter :: ndi=3,ntens=4
real(8) :: D(ntens,ntens),zero_test=0.
D = clcElasticStiffnessMatrix(LAMBDA,MU,ndi,ntens)
contains
function clcElasticStiffnessMatrix(LAMBDA,MU,ndi,ntens)
implicit none
integer :: ndi,ntens,i,j
real(8) :: clcElasticStiffnessMatrix(ntens,ntens),LAMBDA,MU
do i=1,ndi
do j=i,ndi
if(i .eq. j) then
clcElasticStiffnessMatrix(i,j) = 2.*MU + LAMBDA
else
clcElasticStiffnessMatrix(i,j) = LAMBDA
clcElasticStiffnessMatrix(j,i) = clcElasticStiffnessMatrix(i,j)
end if
end do
end do
do i=ndi+1,ntens
do j=i,ntens
if(i .eq. j) then
clcElasticStiffnessMatrix(i,j) = MU
else
clcElasticStiffnessMatrix(i,j) = 0.
clcElasticStiffnessMatrix(j,i) = clcElasticStiffnessMatrix(i,j)
end if
end do
end do
end function
end program
Matrix D in break mode:
D:
174999994368.000 74999996416.0000 74999996416.0000 2.641443384627243E-308
74999996416.0000 174999994368.000 74999996416.0000 2.640433316727162E-308
74999996416.0000 74999996416.0000 174999994368.000 -1.051992669438322E-291
2.640110775815455E-308 0.000000000000000E+000 6.151018477594351E-318 49999998976.0000
The problem is that you are assigning real(4) values to real(8) variables when you write
LAMBDA=75.e9,MU=50.e9
or
clcElasticStiffnessMatrix(i,j) = 0.
Try with d instead e and specify always:
LAMBDA=75.d9,MU=50.d9
clcElasticStiffnessMatrix(i,j) = 0.d0

Subroutine is returning NaN. Class variable issue?

I am having issues with a simple code I have written. subroutine energy is supposed to calculate the energy of the system, and is frequently returning NaN. The reason why it is doing that is because a value dd, distance between particles, is becoming zero and I have no idea why. I am relatively new to Fortran, and I believe maybe it has something to do with how I am passing class variables around which is why I came here. The code and modules are as follows.
module get started defines a class andreads values from an input file and prints to screen.
module get_started
implicit none
type input
integer :: np
integer :: istart
integer :: mceq,mcsteps
integer :: nsample, nadjust
double precision :: rcut,rcut2,rv,rv2,rcrv2
double precision :: maxd
double precision :: vol,box,hbox
double precision :: eps,sig
double precision :: dens
double precision :: temp
end type input
contains
subroutine init(param)
implicit none
type(input) :: param
open(unit=1,file='input')
read(1,*)param%rcut
read(1,*)param%np ! system's size
read(1,*)param%dens ! thermodynamical conditions
read(1,*)param%temp
read(1,*)param%istart
read(1,*)param%maxd
read(1,*)param%mceq,param%mcsteps
read(1,*)param%nsample
read(1,*)param%nadjust
param%eps = 1.0d0
param%sig = 1.0d0
param%rcut2 = param%rcut*param%rcut
param%rv = 1.1*param%rcut
param%rv2 = param%rv*param%rv
param%rcrv2 = (param%rv-param%rcut) * (param%rv-param%rcut)
param%vol = param%np/param%dens
param%box = param%vol**(1.0d0/3.0d0)
param%hbox = param%hbox/2
return
end subroutine init
subroutine print_input(param)
implicit none
type(input):: param
print*
print*,' A Monte Carlo program for Lennard-Jones particles'
print*,' -------------------------------------------------'
print*,' Lennard-Jones parameters:'
print*,' np=',param%np
print*,' eps= ',param%eps
print*,' sig= ',param%sig
print*,' rcut= ',param%rcut
print*,' rv =',param%rv
print*,' rv2 =',param%rv2
print*
print*,' Simulation box length: ',param%box
print*,' Volume : ',param%vol
print*,' Number density : ',param%dens
print*,' Temperature : ',param%temp
print*
print*
print*,' maxd : ',param%maxd
print*,' # eq steps : ',param%mceq
print*,' # MC steps : ',param%mcsteps
print*,' Sampling freq : ',param%nsample
print*,' Adjusting freq : ',param%nadjust
print*
return
end subroutine print_input
end module get_started
module position initializes the positions of the particles
module position
use get_started
implicit none
integer:: numpd
double precision ::gsize
integer :: i,j,k
integer :: flag
contains
subroutine init_position(param,x,y,z,flag)
implicit none
type(input),intent(in) :: param
double precision :: x(param%np),y(param%np),z(param%np)
integer :: counter,flag
numpd = ceiling(param%np**(1.0d0/3.0d0))
print*,'numpd',numpd
print*,'box',param%box
gsize = param%box/numpd
print*,'gsize',gsize
if(flag.eq.0) then
counter =1
do i=1,numpd
do j=1,numpd
do k=1,numpd
if(counter.le.param%np)then
y(counter) = i*gsize
z(counter) = j*gsize
x(counter) = k*gsize
endif
counter = counter+1
enddo
enddo
enddo
else if(flag.eq.1) then
do i=1,param%np
x(i) = ran()*param%box
y(i) = ran()*param%box
z(i) = ran()*param%box
enddo
end if
open(unit=2,file='posit.dat')
do i =1,param%np
write(2,*)x(i),y(i),z(i)
enddo
close(unit=2)
return
end subroutine init_position
end module position
module modmove_energy is where all the action is at. In this module, the subroutines which apply the monte carlo move (picking a particle, random displacement, and accepting or rejecting move) along with the energy calclulation (this is where the error is) are contained. It is the variable dd in energy which is returning zero for non identical particles frequently, and hence causing the NaN error.
module modmove_energy
use get_started
contains
! note random generator function
subroutine move(potential,param,x,y,z,ar)
implicit none
integer :: ar,it,step
type(input),intent(in) :: param
double precision :: potential
double precision :: x(param%np),y(param%np),z(param%np)
double precision :: xold,yold,zold,drxold,dryold,drzold
double precision :: dr2
double precision :: uold,unew,delta
double precision :: alpha,beta,gamma
it = int(ran()*param%np)+1
call energy(uold,x,y,z,param)
alpha =param%maxd*(ran()-0.5)
beta =param%maxd*(ran()-0.5)
gamma =param%maxd*(ran()-0.5)
x(it) = x(it) + alpha
y(it) = y(it) + beta
z(it) = z(it) + gamma
if (x(it) > param%box) then
x(it)=x(it)-param%box
elseif (x(it) < 0.d0) then
x(it)=x(it)+param%box
endif
if (y(it) > param%box) then
y(it)=y(it)-param%box
else if (y(it) < 0.d0) then
y(it)=y(it)+param%box
endif
if (z(it) > param%box) then
z(it)=z(it)-param%box
else if (z(it) < 0.d0) then
z(it)=z(it)+param%box
endif
call energy(unew,x,y,z,param)
delta = unew-uold
if (delta < 0.) then
potential=potential+delta
ar=1
elseif (exp(-delta/(param%temp)) >= ran()) then
potential=potential+delta
ar=1
else
x(it)=xold
y(it)=yold
z(it)=zold
ar=0
endif
end subroutine move
subroutine energy(u,x,y,z,param)
implicit none
integer :: np,it
type(input),intent(in):: param
double precision :: u
double precision,intent(in) :: x(param%np),y(param%np),z(param%np)
integer :: i,j
double precision :: ddx,ddy,ddz,dd
u=0.0e0
do i=1,param%np-1
do j=i+1,param%np
ddx=x(i)-x(j)
ddy=y(i)-y(j)
ddz=z(i)-z(j)
if (ddx > param%hbox) ddx=ddx-param%box
if (ddy > param%hbox) ddy=ddy-param%box
if (ddz > param%hbox) ddz=ddz-param%box
if (ddx < -param%hbox) ddx=ddx+param%box
if (ddy < -param%hbox) ddy=ddy+param%box
if (ddz < -param%hbox) ddz=ddz+param%box
dd=sqrt(ddx*ddx+ddy*ddy+ddz*ddz)
if (dd <= param%rcut) then
u = u + ( (param%sig/dd)**12 - (param%sig/dd)**6 )
endif
enddo
enddo
u=u*4.0*param%eps
return
end subroutine energy
end module modmove_energy
Finally, the main program is as follows
program lennard
use get_started
use position
use modmove_energy
implicit none
integer :: step,ar
integer :: seed1,seed2
type(input) :: param
double precision, allocatable :: x(:),y(:),z(:)
double precision :: potential
seed1 = 2*int(secnds(0.0))
seed2 = 1+seed1
call srand(seed2)
call init(param)
call print_input(param)
allocate (x(param%np),y(param%np),z(param%np))
call init_position(param,x,y,z,0)
do step=1,param%mceq
call move(potential,param,x,y,z,ar)
! print*,potential
enddo
stop
end program lennard
Sorry for taking your guys time, and thank you for looking at this. The error was in subroutine move, I was assigning x(it) = xold, y(it)=yold,z(it)=zold before having defined xold,yold,zold. rookie mistake!