Write in NetCDF as a variable as a function of time - fortran

I'm trying to modify a Fortran 90 code which writes a 2D array to the output in a NetCDF classic format. I would like the variable to have an extra dimension for time (i.e., it will be a 3D variable), printing it every corresponding time step during integration time of the model.
I'm not sure how it is being done; I appreciate any suggestion for doing it as efficiently as possible (also in a minimum file size).
subroutine writenetcdffile(array,argtitle)
use netcdf
implicit none
real, intent(IN), dimension(:,:) :: array
character*(*),intent(IN) :: argtitle
integer :: file_id, xdim_id, ydim_id
integer :: array_id
integer, dimension(2) :: arrdims
! character(len=*) :: argtitle = Flag_in
integer :: i, j
integer :: ierr
i = size(array,1)
j = size(array,2)
! create the file
ierr = nf90_create(path='test.nc', cmode=NF90_CLOBBER, ncid=file_id)
! define the dimensions
ierr = nf90_def_dim(file_id, 'X', i, xdim_id)
ierr = nf90_def_dim(file_id, 'Y', j, ydim_id)
! now that the dimensions are defined, we can define variables on them,...
arrdims = (/ xdim_id, ydim_id /)
ierr = nf90_def_var(file_id, 'Array', NF90_REAL, arrdims, array_id)
! ...and assign units to them as an attribute
ierr = nf90_put_att(file_id, array_id, "title", argtitle)
! done defining
ierr = nf90_enddef(file_id)
! Write out the values
ierr = nf90_put_var(file_id, array_id, array)
! close; done
ierr = nf90_close(file_id)
return
end subroutine writenetcdffile
MODULE Module_NetCDF
use netcdf
IMPLICIT NONE
integer :: file_id, xdim_id, ydim_id, tdim_id
integer :: array_id(5)
integer, dimension(3) :: arrdims
integer :: i, j
integer :: ierr
CONTAINS
SUBROUTINE NetCDF_Init(ICase)
IMPLICIT NONE
INTEGER :: ICase
SELECT CASE(ICase)
Case(1)
! create the file
ierr = nf90_create(path='test.nc', cmode = NF90_CLOBBER, ncid = file_id)
Case(2)
! Reopen the file for writing
ierr = nf90_open(path = "test.nc", mode = nf90_write, ncid = file_id)
if (ierr /= nf90_noerr) call check(ierr)
Case(3)
! close; done
ierr = nf90_close(file_id)
END SELECT
RETURN
END SUBROUTINE NetCDF_Init
SUBROUTINE NetCDF_Def(Array,ArrayTitle,ArrayUnits)
IMPLICIT NONE
real, intent(IN), dimension(:,:) :: Array
character(*),intent(IN) :: ArrayTitle(5)
character(*),intent(IN) :: ArrayUnits(5)
! Locals
integer :: k
i = size(Array,1)
j = size(Array,2)
! CALL NetCDF_Init(1)
! define the dimensions
ierr = nf90_def_dim(file_id, 'X', i, xdim_id)
ierr = nf90_def_dim(file_id, 'Y', j, ydim_id)
ierr = nf90_def_dim(file_id, 'Time', nf90_unlimited, tdim_id)
! now that the dimensions are defined, we can define variables on them,...
arrdims = (/ xdim_id, ydim_id, tdim_id /)
do k = 1,size(ArrayTitle)
ierr = nf90_def_var(file_id, ArrayTitle(k), NF90_REAL, arrdims, array_id(k))
! ...and assign units to them as an attribute
ierr = nf90_put_att(file_id, array_id(k), "Units", ArrayUnits(k))
enddo
! done defining
ierr = nf90_enddef(file_id)
RETURN
END SUBROUTINE NetCDF_Def
SUBROUTINE NetCDF_Write(Array,FlagTitle,NTime)
IMPLICIT NONE
real, intent(IN), dimension(:,:) :: Array
integer,intent(IN) :: NTime
character(*),intent(in) :: FlagTitle
! Locals
integer :: J_id
IF(FlagTitle.EQ.'ONECOND')THEN
J_id = 1
ELSEIF(FlagTitle.EQ.'MELTING')THEN
J_id = 2
ELSEIF(FlagTitle.EQ.'FREEZ_NEW')THEN
J_id = 3
ELSEIF(FlagTitle.EQ.'TFREEZ')THEN
J_id = 4
ELSEIF(FlagTitle.EQ.'DFREEZ')THEN
J_id = 5
ENDIF
CALL NetCDF_Init(2)
ierr = nf90_put_var(file_id, array_id(j_id), Array, start=[1,1,ntime], count=[i,j,1])
CALL NetCDF_Init(3)
RETURN
END SUBROUTINE
SUBROUTINE check(status)
IMPLICIT NONE
integer, intent ( in) :: status
IF(status /= nf90_noerr) THEN
PRINT *, trim(nf90_strerror(status))
STOP 2
ENDIF
END SUBROUTINE check
END MODULE Module_NetCDF

What you need to do is define the time dimension of nf90_unlimited length. This will allow you to write a 2-d array one slice at a time into a 3-d array, and makes the length of this array unspecified. Use start and count optional dummy arguments to a nf90_put_var call to specify where to write the 2-d slice.
! create the file
ierr = nf90_create(path='test.nc', cmode=NF90_CLOBBER, ncid=file_id)
! define the dimensions
ierr = nf90_def_dim(file_id, 'X', i, xdim_id)
ierr = nf90_def_dim(file_id, 'Y', j, ydim_id)
ierr = nf90_def_dim(file_id, 'Time', nf90_unlimited, tdim_id)
! now that the dimensions are defined, we can define variables on them,...
arrdims = (/ xdim_id, ydim_id, tdim_id /)
ierr = nf90_def_var(file_id, 'Array', NF90_REAL, arrdims, array_id)
! done defining
ierr = nf90_enddef(file_id)
! Time loop
do n = 1,nm
! Calculations go here
! Write out the values
ierr = nf90_put_var(file_id, array_id, array, start=[1,1,n], count=[i,j,1])
enddo
What I do in most of my programs is create the file and define dimensions and variables at the beginning, and write the fields in a loop afterward. If your simulations take a long time and you want to be able to look at the output during the simulation in progress, do the open/write/close steps inside of the model solver do-loop.

Related

How to READ data starting from a pattern-matched line with Fortran?

I have a file pos.xyz with the following format, where i = 6,etc represent the frame indices. (Here, the first frame has i = 6. In general, the first frame's index can be i = 0,i = 1, or i = 2,...)
I want to implement a function: For any two given integers a and b,( a<b, e.g., 7 and 9), read
the data from the frame index 7 to 9 into an array. Could you give me a suggestion on how to implement this idea?
4
i = 6, time = 3.000, E = -205.1846561900
O 2.6028572470 4.1666579520 12.7865910725
O 6.5415232423 8.8963227363 17.7533721708
O 15.6020396800 11.9022808314 15.2930838049
O 11.2843786793 13.2653367176 13.8186352548
4
i = 7, time = 3.500, E = -205.1845561905
O 5.1072569275 11.9945026418 4.1254340934
O 2.5299942732 11.4124710424 9.5495912455
O 14.8837181647 12.6571252157 7.8905997802
O 15.1684493877 10.7315923081 2.6631494700
4
i = 8, time = 4.000, E = -205.1846261900
O 2.6028572470 4.1666579520 12.7865910725
O 6.5415232423 8.8963227363 17.7533721708
O 15.6020396800 11.9922808314 15.2930838049
O 11.2843786793 13.2653367176 13.8186352548
4
i = 9, time = 4.500, E = -205.1846561805
O 5.1072569375 11.9945026418 4.1258340934
O 2.5299942732 11.4124710424 9.5495912455
O 14.8837181647 12.6570252157 7.8905997802
O 15.1684493877 10.7310923081 2.6630494700
4
i = 10, time = 5.000, E = -205.1846551805
O 5.1072569275 11.9945026418 4.1254340934
O 2.5299932732 11.4129710424 9.5495912455
O 14.8837181647 12.6571252157 7.8905997802
O 15.1684473877 10.7313923081 2.6631494700
what I did: for the special case with i = 0 as the first frame. For example, If I want to read from the 3rd frame, I can first skip (m+2)*(3-1) lines and then READ the data,m=4. The function is as follows.
SUBROUTINE skip_lines(indx, i_input)
! Purpose:
! To skip lines when read data from the input
IMPLICIT NONE
INTEGER :: i
INTEGER,INTENT(IN) :: i_input,indx
do i=1,i_input
read(indx,*) !Neglect (nat+2)*(ns-1) lines
enddo
END SUBROUTINE skip_lines
But for general case, if the first frame has a frame non-zero number,this idea is not efficient. I hope to find a better way to implement it.
Thanks to #francescalus 's and #High Performance Mark 's suggestions. I use a DO WHILE loop and I have implemented my idea. I put one simplified version of my subroutine here. It include some types defined in modules, which are not the important thing here. Now, it can
(1) Read a trajectory file from any step a to any step b, where a and b are given by user;
(2) Read data every ns steps.
SUBROUTINE read_traj(indx,nmo_start,nmo_end,ns,nat,n_samples)
! goal:
! read info from the trajectory file (format: ***.xyz)
! read data from frame a to frame b
USE atom_module
USE parameter_shared
INTEGER :: iatom, i_sample
INTEGER, PARAMETER:: nat = 4
INTEGER :: n_samples !n_samples = INT((a-b)/ns)
INTEGER, PARAMETER :: indx = 10
INTEGER, PARAMETER :: ns = 2 ! read one sample from the trajectory every ns step.
INTEGER, PARAMETER :: a =7
INTEGER, PARAMETER :: b=10
CHARACTER(LEN=4) :: x
INTEGER :: y
allocate(atom_info(nat,n_samples))
i_sample = 1
DO WHILE (i_sample < n_samples)
read(indx, '(A3,I5)') x, y
CHECK: IF (head_char=="i = " .AND. (y>a-1 .and. y<b+1) .AND. MOD(y-(a-1),ns) == 1) THEN
WRITE(*,*)"head_char and y:", x, y
BACKSPACE(UNIT=indx) ! we have to read the whole line with ' i = ' line.
read(indx,120) sampled_movie(i_sample), sampled_time(i_sample), sampled_energy(i_sample)
120 FORMAT (3X,I5,8X,F9.3,5X,F20.10)
inner: do iatom= 1,nat
read (indx,*) atom_info(iatom, i_sample)%atom_name, atom_info(iatom,i_sample)%coord(1), &
atom_info(iatom,i_sample)%coord(2), atom_info(iatom,i_sample)%coord(3)
enddo inner
i_sample = i_sample + 1
ENDIF CHECK
END DO
END SUBROUTINE read_traj
gfortran -Wall -fcheck=all parameter_shared.f95 atom_module.f95 traj.f95 sample.f95 test.f95 -o test.x
! test.f95
PROGRAM test
! Purpose: To read data starting from any block.
USE atom_module
IMPLICIT NONE
!==========
!parameters
!==========
INTEGER :: ns ! Get one sample from the trajectory every ns step.
INTEGER :: nmo_start
INTEGER :: nmo_end
INTEGER :: nat ! number of atoms
REAL(kind=4) :: delta_t0 ! For reading data
character(LEN=200) :: pos_filename
!===============
! Initialization
delta_t0 = 0.0005; ns = 2
nmo_start = 7; nmo_end = 10
nat = 4; pos_filename="pos.xyz"
!========================
! Sampling the trajectory
CALL sample(pos_filename,nmo_start,nmo_end,nat,ns)
END PROGRAM test
! sample.f95
SUBROUTINE sample(pos_filename,nmo_start,nmo_end,nat,ns)
USE parameter_shared
USE atom_module, ONLY: atom_info
USE traj
IMPLICIT NONE
!==========
!Parameters
!==========
character(LEN=*), INTENT(IN) :: pos_filename
INTEGER, INTENT(IN) :: nmo_start
INTEGER, INTENT(IN) :: nmo_end
INTEGER, INTENT(IN) :: nat ! number of atoms
INTEGER, INTENT(IN) :: ns ! Get one sample from the trajectory every ns step.
!Local varables
INTEGER :: n_samples !n_samples = INT(nmo/ns)
INTEGER :: iatom,imovie,i
!Initialization
iatom = 0; imovie =0; i =0
! Obatin n_samples
n_samples = sampling_number(nmo_start,nmo_end,ns)
allocate(sampled_movie(n_samples))
allocate(sampled_time(n_samples))
allocate(sampled_energy(n_samples))
!=======================
!read in trajectory file
!=======================
open(10,file=trim(pos_filename))
CALL read_traj(10,nmo_start,nmo_end,ns,nat,n_samples)
close(10)
write(6,*) 'End of trajectory reading.'
!=============
!write in file
!=============
sampled_pos_filename = 'pos_sampled.xyz'
open(10,file=sampled_pos_filename)
do i =1,n_samples
write (10,'(I8)') nat
WRITE(10,100) 'i =',i-1,', time =',sampled_time(i),', E =',sampled_energy(i)
100 FORMAT (1X,A3,I10,A8,F10.3,A5,F20.10)
DO iatom = 1, nat
WRITE(10,*) TRIM(atom_info(iatom, i)%atom_name), &
atom_info(iatom,i)%coord(1), &
atom_info(iatom,i)%coord(2), &
atom_info(iatom,i)%coord(3)
ENDDO
enddo
write(6,*)'Sampled trajectory is written in: ', sampled_pos_filename
close(10)
deallocate(sampled_movie, sampled_time,sampled_energy)
END SUBROUTINE sample
MODULE traj
IMPLICIT NONE
CONTAINS
INTEGER FUNCTION sampling_number(nmo_start,nmo_end,ns)
!To calculate the total numbers of samples one want to include
INTEGER,INTENT(IN) :: ns ! Get one sample from the trajectory every ns step.
INTEGER,INTENT(IN) :: nmo_start, nmo_end
write(*,*) 'In function sampling_number: nmo_end = ', nmo_end
positive: IF (nmo_end <0 .OR. nmo_start < 0 .OR. ns <0) THEN
write(*,*) 'Please enter non-negative values for the ns, starting step and ending step.'
ELSE IF (nmo_end < nmo_start) THEN
write(*,*) 'Please note that starting step shoud not larger than ending step.'
ELSE IF (ns ==0) THEN
sampling_number = nmo_end-(nmo_start-1)
ELSE IF (nmo_end-(nmo_start-1) <= ns) THEN
sampling_number = INT((nmo_end-(nmo_start-1))/ns + 1)
ELSE IF (nmo_end-(nmo_start-1) > ns) THEN
sampling_number = INT((nmo_end-(nmo_start-1))/ns)
END IF positive
END FUNCTION sampling_number
SUBROUTINE read_traj(indx,nmo_start,nmo_end,ns,nat,n_samples)
! Purpose: to READ data starting from a pattern-matched line.
USE atom_module, ONLY: atom_info
USE parameter_shared, ONLY: sampled_movie, sampled_time, sampled_energy
INTEGER :: iatom,i_sample
INTEGER, INTENT(IN) :: nat
INTEGER, INTENT(IN) :: n_samples !n_samples = INT(nmo/ns)
INTEGER, INTENT(IN) :: indx
INTEGER, INTENT(IN) :: ns ! Get one sample from the trajectory every ns step.
INTEGER, INTENT(IN) :: nmo_start, nmo_end ! To get the total number of moves
CHARACTER(LEN=4) :: head_char
INTEGER :: y
allocate(atom_info(nat,n_samples))
i_sample = 1
write(*,*) "read_traj(): New total time steps (n_samples):", n_samples
DO WHILE (i_sample < n_samples+1) ! +1 means i_sample can take the value of n_samples
read(indx, '(A4)') head_char
PRE_CHECK:IF (head_char=="i = ") THEN
BACKSPACE(UNIT=indx) ! Because I am not able to read other lines with the format '(A4,I8)', and have not find any good way, so I try to read it in '(A4)' first
read(indx, '(A4,I8)') head_char, y
CHECK_HEAD:IF (head_char=="i = " .AND. (y>nmo_start-1 .and. y<nmo_end+1) .AND. MOD(y-(nmo_start-1),ns) == 1) THEN
WRITE(*,*)"read_traj():", head_char, y
BACKSPACE(UNIT=indx) ! Because we have to read the whole line with ' i = ' line.
read(indx,130) sampled_movie(i_sample), sampled_time(i_sample), sampled_energy(i_sample)
130 FORMAT (4X,I8,9X,F12.3,6X,F20.10)
131 FORMAT (A4,3F20.10)
inner: do iatom= 1,nat
read (indx,131) atom_info(iatom, i_sample)%atom_name, atom_info(iatom,i_sample)%coord(1), &
atom_info(iatom,i_sample)%coord(2), atom_info(iatom,i_sample)%coord(3)
enddo inner
i_sample = i_sample + 1
ENDIF CHECK_HEAD
ENDIF PRE_CHECK
END DO
END SUBROUTINE read_traj
END MODULE traj
MODULE atom_module
! To define the derived data type for atom
IMPLICIT NONE
TYPE :: atom
CHARACTER(LEN=2) :: atom_name
INTEGER :: atom_id
INTEGER :: host_id ! For O atom in water, host_id = atom_id
REAL :: mass
REAL, DIMENSION(3) :: coord
END TYPE atom
! The array atom_info can be shared by subroutines
TYPE(atom), ALLOCATABLE, DIMENSION(:,:) :: atom_info
END MODULE atom_module
MODULE parameter_shared
!
! Purpose:
! To declare data to share between routines.
IMPLICIT NONE
!SAVE
character(LEN=200) :: sampled_pos_filename
INTEGER, ALLOCATABLE, DIMENSION(:) :: sampled_movie
REAL, ALLOCATABLE, DIMENSION(:) :: sampled_time, sampled_energy
END MODULE parameter_shared

NetCDF: Start+count exceeds dimension bound

I have written a code in Fortran to read a NetCDF file that has 4-d data [time, level,longitude,latitude]. However, my code yields an error
NetCDF: Start+count exceeds dimension bound
on any 4-d NetCDF file I am using. For example, the file at http://people.sc.fsu.edu/~jburkardt/f_src/netcdf/pres_temp_4D.nc has pressure and temperature. I paste my code below. Please suggest what is going wrong.
PROGRAM rw_nc4d_main
USE rw_nc4d, ONLY: read_nc4
IMPLICIT NONE
CHARACTER(LEN=50) :: ncfn
CHARACTER(LEN=15) :: vname
ncfn = 'pres_temp_4D.nc'
vname = 'pressure'
CALL read_nc4(ncfn, vname)
END PROGRAM rw_nc4d_main
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
MODULE rw_nc4d
USE netcdf
IMPLICIT NONE
CONTAINS
SUBROUTINE read_nc4(fname,vin_name)
IMPLICIT NONE
CHARACTER(LEN=*), INTENT(IN) :: fname
CHARACTER(LEN=*), INTENT(IN) :: vin_name
! Local variables
INTEGER :: ncid, var_id, ndim, nvar, nattr, unlim_id
CHARACTER(LEN=15) :: dname
INTEGER :: dlength
INTEGER :: ii, status, lx, ly, lz, lt, lzp1
REAL :: sf, ofs
REAL, DIMENSION(:,:,:,:), ALLOCATABLE :: vin
CALL nc_check(nf90_open(fname, nf90_nowrite, ncid))
CALL nc_check(nf90_inquire(ncid,ndim,nvar))
DO ii = 1, ndim
CALL nc_check(nf90_inquire_dimension(ncid,ii,dname,len=dlength))
SELECT CASE(TRIM(dname))
CASE('lon', 'LON', 'longitude')
lx = dlength
CASE('lat', 'LAT', 'latitude' )
ly = dlength
CASE('lev', 'LEV', 'level' )
lz = dlength
CASE('time', 'TIME' )
lt = dlength
CASE('ilev', 'ILEV')
lzp1 = dlength
CASE DEFAULT
WRITE(*,*)'ERROR: nc_check for dimensions!'; STOP
END SELECT
END DO
ALLOCATE(vin(lt,lz,ly,lx))
CALL nc_check(nf90_inq_varid(ncid,TRIM(vin_name),var_id))
CALL nc_check(nf90_get_var(ncid,var_id,vin,start=(/1,1,1,1/),count=(/lt,lz,ly,lx/)),fname=TRIM(fname))
END SUBROUTINE read_nc4
SUBROUTINE nc_check(status,fname)
INTEGER, INTENT(IN) :: status
CHARACTER(LEN=*), OPTIONAL :: fname
IF (status /= nf90_noerr) THEN
IF (PRESENT(fname)) THEN
WRITE(*,*)'FATAL ERROR in ',TRIM(fname),' ',TRIM(nf90_strerror(status))
ELSE
WRITE(*,*)'FATAL ERROR: ',TRIM(nf90_strerror(status))
END IF
STOP
END IF
END SUBROUTINE nc_check
END MODULE rw_nc4d
You have the dimensions back to front. I also suspect that your variable has the longitude and latitude in the reverse order to which you have posted. A variable with shape [time, level,latitude,longitude] should be declared as var(longitude, latitude, level, time) in Fortran.

Error declaring types with kind parameter

With the following program I experience errors.
Program COM
!Input
!No of Atoms
!No of Iterations
!Respective Positions.
!As of now for homogeneous clusters.
Implicit None
Real, Parameter :: R8B=selected_real_kind(10)
Real, Parameter :: R4B=selected_real_kind(4)
Integer, Parameter :: I1B=selected_int_kind(2)
Integer, Parameter :: I2B=selected_int_kind(4)
Integer, Parameter :: I4B=selected_int_kind(9)
Integer, Parameter :: I8B=selected_int_kind(18)
Real (R8B), Dimension (:,:), Allocatable :: Posx, Posy, Posz
Real (R8B), Dimension (:), Allocatable :: Posx_n, Posy_n, Posz_n
Real (R8B), Dimension (:), Allocatable :: dist_com, avj_dist_com
Integer (I4B), Dimension (:), Allocatable :: bin_array
Real (R8B) :: comx, comy, comz
Integer (I8B) :: nIter, nAtom, dist
Integer (I8B) :: I,J,ii,k
Integer (I1B) :: xyz_format, FlagR, FlagM, Flag_com
Integer (I8B) :: bin
Integer (R8B) :: max_dist
Character (50) POS_file, COM_file,Bin_file
Character (2) jj
Read (*,*) POS_file
Read (*,*) COM_file
Read (*,*) Bin_file
Read (*,*) nAtom
Read (*,*) nIter
Read (*,*) xyz_format
Read (*,*) max_dist, bin
! if Flag_com == 1 then compute dist from COM
! if its 0 then specify the atom no and g(r) will be computed..
! i.e. no of atoms from that atom between dist r and r + dr
Allocate (Posx(nAtom,nIter))
Allocate (Posy(nAtom,nIter))
Allocate (Posz(nAtom,nIter))
! xyz_format = 0 ==> old_ks
! xyz_format = 1 ==> xmakemol
! xyz_format = 2 ==> Envision
write(*,*)POS_file
Open (unit=99, file=POS_file)
if (xyz_format == 0 ) then
do i = 1,nIter
read(99,*)
do j = 1,nAtom
read(99,*)ii,Posx(j,i),Posy(j,i),Posz(j,i),ii
enddo
enddo
elseif (xyz_format == 1 ) then
do i = 1,nIter
read(99,*)ii
read(99,*)
do j = 1,nAtom
read(99,*)jj,Posx(j,i),Posy(j,i),Posz(j,i)
enddo
enddo
elseif (xyz_format == 2 ) then
read(99,*)
read(99,*)
read(99,*)
read(99,*)
do i = 1,nIter
do j = 1,nAtom
read(99,*)
read(99,*)Posx(j,i),Posy(j,i),Posz(j,i)
enddo
enddo
endif
Close (99)
Write (*,'(\1x,"Reading Complete")')
allocate (avj_dist_com (nIter))
allocate (dist_com (nAtom))
avj_dist_com = 0.0d0
dist_com = 0.0d0
Allocate (Posx_n(nAtom))
Allocate (Posy_n(nAtom))
Allocate (Posz_n(nAtom))
Allocate (Bin_Array(bin))
Posx_n = 0.0d0
Posy_n = 0.0d0
Posz_n = 0.0d0
bin_array = 0.0d0
Open (unit=2, file=COM_file)
Do I = 1, nIter
comx = 0.0d0
comy = 0.0d0
comz = 0.0d0
Do J = 1, nAtom
comx = comx + Posx(j,i)
comy = comy + Posy(j,i)
comz = comz + Posz(j,i)
Enddo
comx = comx/nAtom
comy = comy/nAtom
comz = comz/nAtom
Write (*,*) i, comx, comy, comz
Do J = 1, nAtom
Posx_n (j) = Posx(j,i) - comx
Posy_n (j) = Posy(j,i) - comy
Posz_n (j) = Posz(j,i) - comz
dist_com (j) = dsqrt ( Posx_n(j)*Posx_n(j) &
+ Posy_n(j)*Posy_n(j) &
+ Posz_n(j)*Posz_n(j) )
avj_dist_com (i) = avj_dist_com(i) + dist_com(j)
Enddo
avj_dist_com(i) = avj_dist_com(i)/nAtom
Do j = 1, nAtom
dist = dist_com (j) * dfloat((bin/max_dist))
bin_array(dist) = bin_array(dist) + 1
Enddo
write (2,'(2x,i6,143(2x,f10.7))') I, avj_dist_com(i),(dist_com(k),k=1,nAtom)
write(*,*) i
Enddo
close (2)
Open (unit=3, file=Bin_file)
do i = 1, bin
write (3,'(2x,i6,4x,i8)') i , bin_array(i)
enddo
close (3)
deAllocate (Posx)
deAllocate (Posy)
deAllocate (Posz)
deAllocate (Posx_n)
deAllocate (Posy_n)
deAllocate (Posz_n)
deallocate (avj_dist_com)
deallocate (dist_com)
deallocate (bin_array)
Stop
End Program COM
The errors look like
Real(KIND=r8b), Dimension (:), Allocatable :: Posx, Posy, Posz
1
Error: Integer expression required at (1)
and there are many more
How can I rectify these?
The kind parameter for a type must be an integer constant expression. You have the latter part down, as you are using named constants R8B and R4B.
However, and this is what the error message says, you have not used an integer constant expression. You should notice that selected_real_kind returns an integer value even as the kind for a selected real type. So, you can correct your code with
Integer, Parameter :: R8B=selected_real_kind(10)
Integer, Parameter :: R4B=selected_real_kind(4)

Sending linked list through MPI

I have seen this question being asked many times, but didn't find an answer that could resolve my issue. I want to be able to send a linked list, in Fortran, to another process through MPI. I have done something similar where the derived data type in the linked list was as follows
type a
{
integer :: varA
type(a), pointer :: next=>null()
real :: varB
}
The way I did this was to create an MPI datatype which contained all the varA values together, and receive it as an array of integers. Then do the same for varB.
What I am trying to do now is to create the linked list, and then pack all the varA and varB values together to form the MPI datatype. I give below the code that does this.
PROGRAM TEST
USE MPI
IMPLICIT NONE
TYPE a
INTEGER:: b
REAL :: e
TYPE(a), POINTER :: nextPacketInList => NULL()
END TYPE
TYPE PacketComm
INTEGER :: numPacketsToComm
TYPE(a), POINTER :: PacketListHeadPtr => NULL()
TYPE(a), POINTER :: PacketListTailPtr => NULL()
END TYPE PacketComm
TYPE(PacketComm), DIMENSION(:), ALLOCATABLE :: PacketCommArray
INTEGER :: packPacketDataType !New data type
INTEGER :: ierr, size, rank, dest, ind
integer :: b
real :: e
CALL MPI_INIT(ierr)
CALL MPI_COMM_SIZE(MPI_COMM_WORLD, size, ierr)
CALL MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr)
IF(.NOT. ALLOCATED(PacketCommArray)) THEN
ALLOCATE(PacketCommArray(0:size-1), STAT=ierr)
DO ind=0, size-1
PacketCommArray(ind)%numPacketsToComm = 0
END DO
ENDIF
b = 2
e = 4
dest = 1
CALL addPacketToList(b, e, dest)
b = 3
e = 5
dest = 1
CALL addPacketToList(b, e, dest)
dest = 1
CALL packPacketList(dest)
IF(rank == 0) THEN
dest = 1
CALL sendPacketList(dest)
ELSE
CALL recvPacketList()
ENDIF
CALL MPI_FINALIZE(ierr)
CONTAINS
SUBROUTINE addPacketToList(b, e, rank)
IMPLICIT NONE
INTEGER :: b, rank, ierr
REAL :: e
TYPE(a), POINTER :: head
IF(.NOT. ASSOCIATED(PacketCommArray(rank)%PacketListHeadPtr)) THEN
ALLOCATE(PacketCommArray(rank)%PacketListHeadPtr, STAT=ierr)
PacketCommArray(rank)%PacketListHeadPtr%b = b
PacketCommArray(rank)%PacketListHeadPtr%e = e
PacketCommArray(rank)%PacketListHeadPtr%nextPacketInList => NULL()
PacketCommArray(rank)%PacketListTailPtr => PacketCommArray(rank)%PacketListHeadPtr
PacketCommArray(rank)%numPacketsToComm = PacketCommArray(rank)%numPacketsToComm+1
ELSE
ALLOCATE(PacketCommArray(rank)%PacketListTailPtr%nextPacketInList, STAT=ierr)
PacketCommArray(rank)%PacketListTailPtr => PacketCommArray(rank)%PacketListTailPtr%nextPacketInList
PacketCommArray(rank)%PacketListTailPtr%b = b
PacketCommArray(rank)%PacketListTailPtr%e = e
PacketCommArray(rank)%PacketListTailPtr%nextPacketInList => NULL()
PacketCommArray(rank)%numPacketsToComm = PacketCommArray(rank)%numPacketsToComm+1
ENDIF
END SUBROUTINE addPacketToList
SUBROUTINE packPacketList(rank)
IMPLICIT NONE
INTEGER :: rank
INTEGER :: numListNodes
INTEGER(kind=MPI_ADDRESS_KIND), DIMENSION(:), ALLOCATABLE :: listNodeAddr
INTEGER(kind=MPI_ADDRESS_KIND), DIMENSION(:), ALLOCATABLE :: listNodeDispl
INTEGER, DIMENSION(:), ALLOCATABLE :: listNodeTypes
INTEGER, DIMENSION(:), ALLOCATABLE :: listNodeCount
TYPE(a), POINTER :: head
INTEGER :: numNode
head => PacketCommArray(rank)%PacketListHeadPtr
numListNodes = PacketCommArray(rank)%numPacketsToComm
PRINT *, ' Number of nodes to allocate for rank ', rank , ' is ', numListNodes
ALLOCATE(listNodeTypes(2*numListNodes), stat=ierr)
ALLOCATE(listNodeCount(2*numListNodes), stat=ierr)
DO numNode=1, 2*numListNodes, 2
listNodeTypes(numNode) = MPI_INTEGER
listNodeTypes(numNode+1) = MPI_REAL
END DO
DO numNode=1, 2*numListNodes, 2
listNodeCount(numNode) = 1
listNodeCount(numNode+1) = 1
END DO
ALLOCATE(listNodeAddr(2*numListNodes), stat=ierr)
ALLOCATE(listNodeDispl(2*numListNodes), stat=ierr)
numNode = 1
DO WHILE(ASSOCIATED(head))
CALL MPI_GET_ADDRESS(head%b, listNodeAddr(numNode), ierr)
CALL MPI_GET_ADDRESS(head%e, listNodeAddr(numNode+1), ierr)
numNode = numNode + 2
head => head%nextPacketInList
END DO
DO numNode=1, UBOUND(listNodeAddr,1)
listNodeDispl(numNode) = listNodeAddr(numNode) - listNodeAddr(1)
END DO
CALL MPI_TYPE_CREATE_STRUCT(UBOUND(listNodeAddr,1), listNodeCount, listNodeDispl, listNodeTypes, packPacketDataType, ierr)
CALL MPI_TYPE_COMMIT(packPacketDataType, ierr)
END SUBROUTINE packPacketList
SUBROUTINE sendPacketList(rank)
IMPLICIT NONE
INTEGER :: rank, ierr, numNodes
TYPE(a), POINTER :: head
head => PacketCommArray(rank)%PacketListHeadPtr
numNodes = PacketCommArray(rank)%numPacketsToComm
CALL MPI_SSEND(head%b, 1, packPacketDataType, rank, 0, MPI_COMM_WORLD, ierr)
END SUBROUTINE sendPacketList
SUBROUTINE recvPacketList
IMPLICIT NONE
TYPE(a), POINTER :: head
TYPE(a), DIMENSION(:), ALLOCATABLE :: RecvPacketCommArray
INTEGER, DIMENSION(:), ALLOCATABLE :: recvB
INTEGER :: numNodes, ierr, numNode
INTEGER, DIMENSION(MPI_STATUS_SIZE):: status
head => PacketCommArray(rank)%PacketListHeadPtr
numNodes = PacketCommArray(rank)%numPacketsToComm
ALLOCATE(RecvPacketCommArray(numNodes), stat=ierr)
ALLOCATE(recvB(numNodes), stat=ierr)
CALL MPI_RECV(RecvPacketCommArray, 1, packPacketDataType, 0, 0, MPI_COMM_WORLD, status, ierr)
DO numNode=1, numNodes
PRINT *, ' value in b', RecvPacketCommArray(numNode)%b
PRINT *, ' value in e', RecvPacketCommArray(numNode)%e
END DO
END SUBROUTINE recvPacketList
END PROGRAM TEST
So basically I create a linked list with two nodes containing the following data
Node 1
b = 2, e = 4
Node 2
b = 3, e = 5
When I run this code on two cores, the results I get on core 1 are
value in b 2
value in e 4.000000
value in b 0
value in e 0.0000000E+00
So my code seems to send the data in the first node of the linked list correctly, but not the second one. Please could someone let me know if what I am trying to do is feasible, and what is wrong with the code. I know I can send the values of b in all nodes together and then the values of e together. But my derived data type will probably contain more variables (including arrays) and I want to be able to send all the data in one go instead of using multiple sends.
Thanks
It's not easy for me to read that code, but it seems like you are expecting the receiving buffer to get contiguous data, which is not the case. The strange type you construct by computing address offsets is not going to match the receiving buffer. To illustrate this, I though I might present this simple example (it's quickly written, don't take it as a good code example):
program example
use mpi
integer :: nprocs, myrank
integer :: buf(4)
integer :: n_elements
integer :: len_element(2)
integer(MPI_ADDRESS_KIND) :: disp_element(2)
integer :: type_element(2)
integer :: newtype
integer :: istat(MPI_STATUS_SIZE)
integer :: ierr
call mpi_init(ierr)
call mpi_comm_size(mpi_comm_world, nprocs, ierr)
call mpi_comm_rank(mpi_comm_world, myrank, ierr)
! simple example illustrating mpi_type_create_struct
! take an integer array buf(4):
! on rank 0: [ 7, 2, 6, 4 ]
! on rank 1: [ 1, 1, 1, 1 ]
! and we create a struct to send only elements 1 and 3
! so that on rank 1 we'll get [7, 1, 6, 1]
if (myrank == 0) then
buf = [7, 2, 6, 4]
else
buf = 1
end if
n_elements = 2
len_element = 1
disp_element(1) = 0
disp_element(2) = 8
type_element = MPI_INTEGER
call mpi_type_create_struct(n_elements, len_element, disp_element, type_element, newtype, ierr)
call mpi_type_commit(newtype, ierr)
write(6,'(1x,a,i2,1x,a,4i2)') 'SEND| rank ', myrank, 'buf = ', buf
if (myrank == 0) then
call mpi_send (buf, 1, newtype, 1, 13, MPI_COMM_WORLD, ierr)
else
call mpi_recv (buf, 1, newtype, 0, 13, MPI_COMM_WORLD, istat, ierr)
!the below call does not scatter the received integers, try to see the difference
!call mpi_recv (buf, 2, MPI_INTEGER, 0, 13, MPI_COMM_WORLD, istat, ierr)
end if
write(6,'(1x,a,i2,1x,a,4i2)') 'RECV| rank ', myrank, 'buf = ', buf
end program
I hope this clearly shows that the receiving buffer will have to accommodate any offsets in the constructed type, and does not receive any contiguous data.
EDIT: updated the code to illustrate a different receive type that does not scatter the data.

Passing different set of variables in a FORTRAN subroutine

I want to apply three different methods, selected with the value of an integer switch. The first method uses two integers, the second a real array and an integer and the third a real 2D array. In my current implementation, I allocate and pass as parameters all the above data (2 int + real_array + int + real_2array). I could also use a module, but it would be similar. I'm searching for a method to define only the data that my method will use (i.e. only the matrix for method 3) and nothing else. Any suggestions?
Edit:
I have made a simplified version of what I described above.
A part of the main program:
INTEGER :: m, imeth
REAL*8 :: x, y
REAL*8, DIMENSION(:), ALLOCATABLE :: uu, wc
REAL*8, DIMENSION(:,:), ALLOCATABLE :: BCH
imeth = 0
m = 64
ALLOCATE(uu(m), uu_(m), wc(m))
ALLOCATE(BCH(m,m))
if (imeth .EQ. 0) then
x = 1.0d0
y = 2.0d0
elseif (imeth .EQ. 1) then
!Assign values to wc
else
!Assign values to BCH
endif
call subr(m,x,y,uu,uu_,imeth,BCH,wc)
STOP
END
and a subroutine
SUBROUTINE subr(n,a,b,u,u_,imeth,DCH,ws)
IMPLICIT NONE
INTEGER, INTENT(IN) :: n, imeth
REAL*8, INTENT(IN) :: u(n), DCH(n,n), ws(n)
REAL*8, INTENT(OUT) :: u_(n)
INTEGER :: i
if (imeth .EQ. 0) then
u_ = -u_ * 0.5d0 / (a+b)
elseif (imeth .EQ. 1) then
u_ = -u / ws
else
u_ = matmul(DCH,u)
endif
RETURN
END SUBROUTINE subr
I want the main program to have a form like
imeth = 0
m = 64
ALLOCATE(uu(m), uu_(m))
if (imeth .EQ. 0) then
a = 1.0d0
b = 2.0d0
elseif (imeth .EQ. 1) then
ALLOCATE(wc(m))
!Assign values to wc
else
ALLOCATE(BCH(m,m))
!Assign values to BCH
endif
if (imeth .EQ. 0) then
call subrA(m,x,y,uu,uu_)
elseif (imeth .EQ. 1) then
call subrB(m,wc,uu,uu_)
else
call subrC(m,BCH,uu,uu_)
endif
EDIT: After OP added the code I think that using optional arguments in conjunction with the present intrinsic might be better suited for this task. The subroutine could then read
SUBROUTINE subr(n,u_,a,b,u,DCH,ws)
IMPLICIT NONE
INTEGER, INTENT(IN) :: n
REAL*8, INTENT(OUT) :: u_(n)
REAL*8, INTENT(IN),OPTIONAL :: a(n)
REAL*8, INTENT(IN),OPTIONAL :: b(n)
REAL*8, INTENT(IN),OPTIONAL :: u(n)
REAL*8, INTENT(IN),OPTIONAL :: DCH(n,n)
REAL*8, INTENT(IN),OPTIONAL :: ws(n)
INTEGER :: i
if ( present(a) .and. present(b) ) then
u_ = -u_ * 0.5d0 / (a+b)
elseif ( present(u) .and. present(ws) ) then
u_ = -u / ws
elseif ( present(wch) .and. present(u) ) then
u_ = matmul(DCH,u)
else
stop 'invalid combination'
endif
END SUBROUTINE subr
Here is the old answer as it still might be helpful:
Maybe you could try interfaces:
module interface_test
implicit none
interface method
module procedure method1
module procedure method2
module procedure method3
end interface
contains
subroutine method1(int1, int2)
implicit none
integer,intent(in) :: int1
integer,intent(out) :: int2
int2 = 2*int1
end subroutine
subroutine method2(int, realArray)
implicit none
integer,intent(in) :: int
real,intent(out) :: realArray(:)
realArray = real(2*int)
end subroutine
subroutine method3(realArray)
implicit none
real,intent(inout) :: realArray(:,:)
realArray = 2*realArray
end subroutine
end module
program test
use interface_test, only: method
implicit none
integer :: int1, int2
real :: arr1D(10)
real :: arr2D(10,10)
int1 = 1
call method(int1, int2)
print *, int2
call method(int1,arr1D)
print *, arr1D(1)
arr2D = 1.
call method(arr2D)
print *, arr2D(1,1)
end program