I have a subroutine that opens and reads a file. The final result is an array that contains the data from the input file in a re-organized fashion. I want to call the subroutine in the main program to use the aforementioned array.
The subroutine has all the variables necessary for it to run as a separate program declared in its file. I'm new using Fortran so I'm not sure how to correctly employ subroutines. Do I need to assign any formal variables to subroutine's first line, or should I have an empty set of parenthesis?
The subroutine is in a file (subroutine.f03) that's separate from the main program's file (main.f03).
Main program code:
PROGRAM main
IMPLICIT NONE
CALL readBasis
WRITE(*,*) basis(1,1)
END PROGRAM
Subroutine code:
SUBROUTINE readBasis()
IMPLICIT NONE
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! io_open = IOSTATUS FOR OPENING THE BASIS FILE !!
!! io_red = IOSTATUS FOR READING THE BASIS FILE !!
!! atom_num = NUMBER ASSIGNED TO A PARTICULAR ATOM IN THE BASIS FILE !!
!! end_of_line = 0, DEFAULT BASIS SET INPUT FORMAT !!
!! end_of_line_1 = 0.00 DEFAULT BASIS SET INPUT FORMAT !!
!! atom_end = **** INDICATES THE END OF THE BASIS SET INFO FOR A GIVEN ATOM !!
!! primitives = NUMBER OF PRIMITIVES IN A CONTRACTION !!
!! basis_type = ANGULAR MOMENTUM ASSOCIATED WITH A CONTRACTION !!
!! expo = GAUSSIAN PRIMITIVE EXPONENT !!
!! coeff = CONTRACTION COEFFICIENT FOR AN S, P, D PRIMITIVE RESPECTIVELY IN A S, P, D SHELL !!
!! s_coeff & p_coeff = CONTRACTION COEFFICIENTS FOR S AND P PRIMITIVES IN AN SP SHELL !!
!! basis = ARRAY CONTAINING ALL OF THE BASIS SET INFORMATION. THE FORMAT IS GIVEN BELLOW: !!
!! BASIS NUMBER | PRIMITIVE TYPE | EXPONENT | S COEFF | P COEFF | D COEFF | X COORDS | Y COORDS | Z COORDS !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
INTEGER :: i, io_open, io_read, atom_num, end_of_line, primitives, gauss_i, gauss_f
INTEGER :: total_basis_functions, total_primitives, primitive_counter, primitive_num
INTEGER :: func_start, func_end, func_counter
CHARACTER (LEN=4) :: basis_type, atom_end
REAL :: scaling, end_of_line_1
REAL :: expo, coeff, s_coeff, p_coeff
REAL, ALLOCATABLE :: basis(:,:)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! atom_loop WILL LET YOU READ THE BASIS FUNCTIONS FOR EVERY ATOM !!
!! contraction_loop WILL LET YOU READ EACH BASIS FUNCTION PER ATOM !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
OPEN(UNIT=10, FILE="BASIS", STATUS="OLD", ACTION="READ", IOSTAT=io_open)
READ(10,*) total_basis_functions
READ(10,*) total_primitives
ALLOCATE(basis(total_primitives,6))
READ(10,*,IOSTAT=io_read) atom_num, end_of_line
READ(10,*) basis_type, primitives, scaling, end_of_line_1, func_start, func_end
atom_end = basis_type
primitive_num = 1
atom_loop: DO WHILE (io_read .EQ. 0)
contraction_loop: DO WHILE (atom_end .NE. "****")
orbital_type_loop: IF (basis_type == "S ") THEN
DO func_counter = func_start, func_end
DO primitive_counter = 1, primitives
READ(10,*) expo, coeff
basis(primitive_num,1) = REAL(func_counter)
basis(primitive_num,2) = REAL(0)
basis(primitive_num,3) = expo
basis(primitive_num,4) = coeff
basis(primitive_num,5) = REAL(0)
basis(primitive_num,6) = REAL(0)
primitive_num = primitive_num + 1
END DO
IF (func_counter .LT. func_end) THEN
DO primitive_counter = 1, primitives
BACKSPACE(10)
END DO
ELSE
CONTINUE
END IF
END DO
ELSE IF (basis_type .EQ. "P ") THEN
DO func_counter = func_start, func_end
DO primitive_counter = 1, primitives
READ(10,*) expo, coeff
basis(primitive_num,1) = REAL(func_counter)
basis(primitive_num,2) = REAL(1)
basis(primitive_num,3) = expo
basis(primitive_num,4) = REAL(0)
basis(primitive_num,5) = coeff
basis(primitive_num,6) = REAL(0)
primitive_num = primitive_num + 1
END DO
IF (func_counter .LT. func_end) THEN
DO primitive_counter = 1, primitives
BACKSPACE(10)
END DO
ELSE
CONTINUE
END IF
END DO
ELSE IF (basis_type == "D ") THEN
DO func_counter = func_start, func_end
DO primitive_counter = 1, primitives
READ(10,*) expo, coeff
basis(primitive_num, 1) = REAL(func_counter)
basis(primitive_num,2) = REAL(2)
basis(primitive_num,3) = expo
basis(primitive_num,4) = REAL(0)
basis(primitive_num,5) = REAL(0)
basis(primitive_num,6) = coeff
primitive_num = primitive_num + 1
END DO
IF (func_counter .LT. func_end) THEN
DO primitive_counter = 1, primitives
BACKSPACE(10)
END DO
ELSE
CONTINUE
END IF
END DO
ELSE IF (basis_type .EQ. "SP ") THEN
DO func_counter = func_start, func_end
DO primitive_counter = 1, primitives
READ(10,*) expo, s_coeff, p_coeff
basis(primitive_num,1) = REAL(func_counter)
basis(primitive_num,2) = REAL(10)
basis(primitive_num,3) = expo
basis(primitive_num,4) = s_coeff
basis(primitive_num,5) = p_coeff
basis(primitive_num,6) = REAL(0)
primitive_num = primitive_num + 1
END DO
IF (func_counter .LT. func_end) THEN
DO primitive_counter = 1, primitives
BACKSPACE(10)
END DO
ELSE
CONTINUE
END IF
END DO
END IF orbital_type_loop
READ(10,*) atom_end
IF (atom_end .EQ. "****") THEN
READ(10,*,IOSTAT=io_read) atom_num, end_of_line
IF (io_read < 0) THEN
EXIT atom_loop
ELSE IF (io_read > 0) THEN
WRITE(*,*) "FILE COULD NOT BE READ."
EXIT atom_loop
ELSE
READ(10,*) basis_type, primitives, scaling, end_of_line_1, func_start, func_end
atom_end = basis_type
EXIT contraction_loop
END IF
ELSE
BACKSPACE(10)
READ(10,*) basis_type, primitives, scaling, end_of_line_1, func_start, func_end
END IF
END DO contraction_loop
END DO atom_loop
CLOSE(10)
RETURN
END SUBROUTINE
A subroutine has "dummy variables" identified in the parenthesis at its inception. These can be input or output arguements of mixed data types, i.e. a mixture of integers, integer arrays, reals ,etc.. Each dummy variable must have a data type assigned to in the variable declarations section of the subroutine, before any procedural statements. It is good practice, IMO, to use the intent modifier to ensure clarity between input and output varaibles. Varaibles that exist locally in the subroutine and are not explicitly input or ouput do not need to be in the parens but do do need to be declared, unless they have an implicit data type. Here is an example:
subroutine MEGA_SUBROUTINE(X,Y,Z,OUTPUT_ARRAY)
implicit none
real, intent(in):: X,Y,Z
real:: local_var
real, intent(out):: OUTPUT_ARRAY
! begin procedural section
! do stuff with your variables here, assign a value to output array
end subroutine MEGA_SUBROUTINE
Related
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
I'm trying to figure out how to properly normalize the results of a DFT using FFTW. The FFTW tutorial states that the forward (FFTW_FORWARD) discrete Fourier transform of a 1d complex array X of size n computes an array Y, where
Y_k = \sum\limits_{j=0}^{n-1} X_j e^{-2\pi j k \sqrt{-1}/n}
The backward DFT computes:
Y_k = \sum\limits_{j=0}^{n-1} X_j e^{+2\pi j k \sqrt{-1}/n}
These definitions are the same as for real-to-complex transformations.
Furthermore, the tutorial specifies that "FFTW computes an unnormalized transform, in that there is no coefficient in front of the summation in the DFT. In other words, applying the forward and then the backward transform will multiply the input by n." However, it doesn't specify where exactly this re-scaling needs to be done. I suppose this may be application dependant, but am not sure how to use it properly. This answer states that it should be normalized in the forward direction, but I have my doubts, which I will elaborate.
My goal is to figure out how to properly normalize the FFT results in order to get what I expect. So I did a simple 1D transformation first, where I know what to expect exactly: Using the same convention as FFTW (normalisation factor=1, oscillatory factor=-2*pi for the forward fourier transform), when I transform
1/2 (δ(1 + x) - δ(1 - x))
with δ being the dirac delta function, I expect to get:
integral_(-∞)^∞ (1/2 (δ(1 + x) - δ(1 - x))) e^(-2 π i ω x) dx = i sin(2π ω)
the same holds for when I do an IFFT on i sin(2π ω), only now I need to normalize by dividing by n.
Here is the code I use to demonstrate this behaviour:
program use_fftw
use,intrinsic :: iso_c_binding
implicit none
include 'fftw3.f03'
integer, parameter :: N = 1000
integer, parameter :: dp = kind(1.d0)
real(dp), parameter :: pi = 3.1415926d0
real(dp), parameter :: physical_length = 500
real(dp), parameter :: dx = physical_length/real(N)
real(dp), parameter :: dk = 1.d0 / physical_length
integer :: i, ind1, ind2
! for double precision: use double complex & call dfftw_plan_dft_1d
complex(C_DOUBLE_COMPLEX), allocatable, dimension(:) :: arr_out
real(C_DOUBLE), allocatable, dimension(:) :: arr_in
type(C_PTR) :: plan_forward, plan_backward
allocate(arr_in(1:N))
allocate(arr_out(1:N/2+1))
plan_forward = fftw_plan_dft_r2c_1d(N, arr_in, arr_out, FFTW_ESTIMATE)
plan_backward = fftw_plan_dft_c2r_1d(N, arr_out, arr_in, FFTW_ESTIMATE)
!----------------------
! Setup
!----------------------
! add +1: index = 1 corresponds to x=0
ind1 = int(1.d0/dx)+1 ! index where x=1
ind2 = int((physical_length-1.d0)/dx)+1 ! index where x=-1
arr_in = 0
arr_in(ind1) = -0.5d0
arr_in(ind2) = 0.5d0
!----------------------
! Forward
!----------------------
call fftw_execute_dft_r2c(plan_forward, arr_in, arr_out)
write(*,*) "Verification: Max real part of arr_out:", maxval(real(arr_out))
open(unit=666,file='./fftw_output_norm1d_fft.txt', form='formatted')
do i = 1, N/2+1
write(666, '(2E14.5,x)') (i-1)*dk, aimag(arr_out(i))
enddo
close(666)
write(*,*) "Finished! Written results to fftw_output_norm1d_fft.txt"
!----------------------
! Backward
!----------------------
call fftw_execute_dft_c2r(plan_backward, arr_out, arr_in)
arr_in = arr_in/N
open(unit=666,file='./fftw_output_norm1d_real.txt', form='formatted')
do i = 1, N
write(666, '(2E14.5,x)') (i-1)*dx, arr_in(i)
enddo
close(666)
write(*,*) "Finished! Written results to fftw_output_norm1d_real.txt"
deallocate(arr_in, arr_out)
call fftw_destroy_plan(plan_forward)
call fftw_destroy_plan(plan_backward)
end program use_fftw
And the results, perfectly according to what I'd expect:
So in this case, I only normalized (division by n) when going from Fourier space back to real space and obtained what I wanted.
But I ran into problems when I tried to do the same for multiple dimensions.
This time, I'm trying to transform
sqrt(π/2) ((δ(-1 + x) - δ(1 + x)) δ(y) + δ(x) (δ(-1 + y) - δ(1 + y)))
which should give
integral_(-∞)^∞ (sqrt(π/2) ((δ(-1 + x) - δ(1 + x)) δ(y) + δ(x) (δ(-1 + y) - δ(1 + y)))) e^(-2 π i {x, y} {a, b}) d{x, y} = +i sin(a) + i sin(b)
I plot the results for x=0 (k_x = 0, respectively):
which seems completely wrong, both in frequency of the sinus wave and the amplitude.
However, transforming back and normalising by dividing by n^2 gives the expected initial conditions, in both x and y direction. Here is the plot for x=0:
I have no idea what I am doing wrong...
Here is the 2d code:
program use_fftw
use,intrinsic :: iso_c_binding
implicit none
include 'fftw3.f03'
integer, parameter :: N = 1000
integer, parameter :: dp = kind(1.d0)
real(dp), parameter :: pi = 3.1415926d0
real(dp), parameter :: physical_length = 500
real(dp), parameter :: dx = physical_length/real(N)
real(dp), parameter :: dk = 1.d0 / physical_length
integer :: i, ind1, ind2
! for double precision: use double complex & call dfftw_plan_dft_1d
complex(C_DOUBLE_COMPLEX), allocatable, dimension(:,:) :: arr_out
real(C_DOUBLE), allocatable, dimension(:,:) :: arr_in
type(C_PTR) :: plan_forward, plan_backward
allocate(arr_in(1:N, 1:N))
allocate(arr_out(1:N/2+1, 1:N))
plan_forward = fftw_plan_dft_r2c_2d(N, N, arr_in, arr_out, FFTW_ESTIMATE)
plan_backward = fftw_plan_dft_c2r_2d(N, N, arr_out, arr_in, FFTW_ESTIMATE)
!----------------------
! Setup
!----------------------
! add +1: index = 1 corresponds to x=0
ind1 = int(1.d0/dx)+1 ! get index where x = 1
ind2 = int((physical_length-1.d0)/dx)+1 ! get index where x = -1
arr_in = 0
! y=0:
arr_in(ind1, 1) = sqrt(pi/2)
arr_in(ind2, 1) = -sqrt(pi/2)
! x=0:
arr_in(1, ind1) = sqrt(pi/2)
arr_in(1, ind2) = -sqrt(pi/2)
!----------------------
! Forward
!----------------------
call fftw_execute_dft_r2c(plan_forward, arr_in, arr_out)
write(*,*) "Verification: Max real part of arr_out:", maxval(real(arr_out))
open(unit=666,file='./fftw_output_norm2d_fft_x=0.txt', form='formatted')
open(unit=667,file='./fftw_output_norm2d_fft_y=0.txt', form='formatted')
do i = 1, N
write(666, '(2E14.5,x)') (i-1)*dk, aimag(arr_out(1,i))
enddo
do i = 1, N/2+1
write(667, '(2E14.5,x)') (i-1)*dk, aimag(arr_out(i,1))
enddo
close(666)
close(667)
write(*,*) "Finished! Written results to fftw_output_normalisation_fft_x.txt and fftw_output_normalisation_fft_y.txt"
!----------------------
! Backward
!----------------------
call fftw_execute_dft_c2r(plan_backward, arr_out, arr_in)
! Normalisation happens here!
arr_in = arr_in/N**2
open(unit=666,file='./fftw_output_norm2d_real_x=0.txt', form='formatted')
open(unit=667,file='./fftw_output_norm2d_real_y=0.txt', form='formatted')
do i = 1, N
write(666, '(2E14.5,x)') (i-1)*dx, arr_in(1, i)
write(667, '(2E14.5,x)') (i-1)*dx, arr_in(i, 1)
enddo
close(666)
close(667)
write(*,*) "Finished! Written results to fftw_output_norm2d_real_x=0.txt and fftw_output_norm2d_real_y=0.txt"
deallocate(arr_in, arr_out)
call fftw_destroy_plan(plan_forward)
call fftw_destroy_plan( plan_backward)
end program use_fftw
and a python plotting tool:
#!/usr/bin/python3
#====================================
# Plots the results of the FFTW
# example programs.
#====================================
import numpy as np
import matplotlib.pyplot as plt
from sys import argv
from time import sleep
errormessage="""
I require an argument: Which output file to plot.
Usage: ./plot_fftw.py <case>
options for case:
1 fftw_output_norm1d_fft.txt
2 fftw_output_norm1d_real.txt
3 fftw_output_norm2d_fft_x=0.txt
4 fftw_output_norm2d_real_x=0.txt
5 fftw_output_norm2d_fft_y=0.txt
6 fftw_output_norm2d_real_y=0.txt
Please select a case: """
#----------------------
# Hardcoded stuff
#----------------------
file_dict={}
file_dict['1'] = ('fftw_output_norm1d_fft.txt', '1d Fourier transform')
file_dict['2'] = ('fftw_output_norm1d_real.txt', '1d Full circle')
file_dict['3'] = ('fftw_output_norm2d_fft_x=0.txt', '2d Fourier transform, x=0')
file_dict['4'] = ('fftw_output_norm2d_real_x=0.txt', '2d Full circle, x=0')
file_dict['5'] = ('fftw_output_norm2d_fft_y=0.txt', '2d Fourier transform, y=0')
file_dict['6'] = ('fftw_output_norm2d_real_y=0.txt', '2d Full circle, y=0')
#------------------------
# Get case from cmdline
#------------------------
case = ''
def enforce_integer():
global case
while True:
case = input(errormessage)
try:
int(case)
break
except ValueError:
print("\n\n!!! Error: Case must be an integer !!!\n\n")
sleep(2)
if len(argv) != 2:
enforce_integer()
else:
try:
int(argv[1])
case = argv[1]
except ValueError:
enforce_integer()
filename,title=file_dict[case]
#-------------------------------
# Read and plot data
#-------------------------------
k, Pk = np.loadtxt(filename, dtype=float, unpack=True)
fig = plt.figure()
ax = fig.add_subplot(111)
# ax.plot(k, Pk, label='power spectrum')
if case in ['1', '3', '5']:
ax.plot(k, Pk, label='recovered wave', lw=3) # ignore negative k
x = np.linspace(k.min(), k.max(), 1000)
if case=='1':
ax.plot(x, np.sin(2*np.pi*x), ':', label='expected wave', lw=3)
if case in ['3', '5']:
ax.plot(x, np.sin(x), ':', label='expected wave', lw=3)
ax.set_title(title)
ax.set_xlabel("k")
ax.set_ylabel("F(k)")
if case in ['2', '4', '6']:
# in this case: k=x, Pk=f(x)
ax.plot(k, Pk, label='recovered original', lw=3) # ignore negative k
N=1000
plen=500
dx=plen/N
x = np.linspace(k.min(), k.max(), 1000)
y = np.zeros(1000)
ind = int(1.0/dx)
if case=='2':
y[ind] = -0.5
y[-ind] = 0.5
if case in ['4', '6']:
y[ind] = np.sqrt(np.pi/2)
y[-ind] = -np.sqrt(np.pi/2)
ax.plot(x, y, ':', label='expected original', lw=3)
ax.set_title(title)
ax.set_xlabel("x")
ax.set_ylabel("f(x)")
ax.legend()
plt.show()
I'm trying to learn Fortran (unfortunately a necessity for my research group) - one of the tasks I set myself was to package one of the necessary functions (Associated Legendre polynomials) from the Numerical Recipes book into a fortran 03 compliant module. The original program (f77) has some error handling in the form of the following:
if(m.lt.0.or.m.gt.1.or.abs(x).gt.1)pause 'bad arguments in plgndr'
Pause seems to have been deprecated since f77 as using this line gives me a compiling error, so I tried the following:
module sha_helper
implicit none
public :: plgndr, factorial!, ylm
contains
! numerical recipes Associated Legendre Polynomials rewritten for f03
function plgndr(l,m,x) result(res_plgndr)
integer, intent(in) :: l, m
real, intent(in) :: x
real :: res_plgndr, fact, pll, pmm, pmmp1, somx2
integer :: i,ll
if (m.lt.0.or.m.gt.l.or.abs(x).gt.1) then
write (*, *) "bad arguments to plgndr, aborting", m, x
res_plgndr=-10e6 !return a ridiculous value
else
pmm = 1.
if (m.gt.0) then
somx2 = sqrt((1.-x)*(1.+x))
fact = 1.
do i = 1, m
pmm = -pmm*fact*somx2
fact = fact+2
end do
end if
if (l.eq.m) then
res_plgndr = pmm
else
pmmp1 = x*(2*m+1)*pmm
if(l.eq.m+1) then
res_plgndr = pmmp1
else
do ll = m+2, l
pll = (x*(2*ll-1)*pmmp1-(ll+m-1)*pmm)/(ll-m)
pmm = pmmp1
pmmp1 = pll
end do
res_plgndr = pll
end if
end if
end if
end function plgndr
recursive function factorial(n) result(factorial_result)
integer, intent(in) :: n
integer, parameter :: RegInt_K = selected_int_kind(20) !should be enough for the factorials I am using
integer (kind = RegInt_K) :: factorial_result
if (n <= 0) then
factorial_result = 1
else
factorial_result = n * factorial(n-1)
end if
end function factorial
! function ylm(l,m,theta,phi) result(res_ylm)
! integer, intent(in) :: l, m
! real, intent(in) :: theta, phi
! real :: res_ylm, front_block
! real, parameter :: pi = 3.1415926536
! front_block = sqrt((2*l+1)*factorial(l-abs(m))/(4*pi*))
! end function ylm
end module sha_helper
The main code after the else works, but if I execute my main program and call the function with bad values, the program freezes before executing the print statement. I know that the print statement is the problem, as commenting it out allows the function to execute normally, returning -10e6 as the value. Ideally, I would like the program to crash after giving a user readable error message, as giving bad values to the plgndr function is a fatal error for the program. The function plgndr is being used by the program sha_lmc. Currently all this does is read some arrays and then print a value of plgndr for testing (early days). The function ylm in the module sha_helper is also not finished, hence it is commented out. The code compiles using gfortran sha_helper.f03 sha_lmc.f03 -o sha_lmc, and
gfortran --version
GNU Fortran (GCC) 4.8.2
!Spherical Harmonic Bayesian Analysis testbed for Lagrangian Dynamical Monte Carlo
program sha_analysis
use sha_helper
implicit none
!Analysis Parameters
integer, parameter :: harm_order = 6
integer, parameter :: harm_array_length = (harm_order+1)**2
real, parameter :: coeff_lo = -0.1, coeff_hi = 0.1, data_err = 0.01 !for now, data_err fixed rather than heirarchical
!Monte Carlo Parameters
integer, parameter :: run = 100000, burn = 50000, thin = 100
real, parameter :: L = 1.0, e = 1.0
!Variables needed by the program
integer :: points, r, h, p, counter = 1
real, dimension(:), allocatable :: x, y, z
real, dimension(harm_array_length) :: l_index_list, m_index_list
real, dimension(:,:), allocatable :: g_matrix
!Open the file, allocate the x,y,z arrays and read the file
open(1, file = 'Average_H_M_C_PcP_boschi_1200.xyz', status = 'old')
read(1,*) points
allocate(x(points))
allocate(y(points))
allocate(z(points))
print *, "Number of Points: ", points
readloop: do r = 1, points
read(1,*) x(r), y(r), z(r)
end do readloop
!Set up the forwards model
allocate(g_matrix(harm_array_length,points))
!Generate the l and m values of spherical harmonics
hloop: do h = 0, harm_order
ploop: do p = -h,h
l_index_list(counter) = h
m_index_list(counter) = p
counter = counter + 1
end do ploop
end do hloop
print *, plgndr(1,2,0.1)
!print *, ylm(1,1,0.1,0.1)
end program sha_analysis
Your program does what is known as recursive IO - the initial call to plgndr is in the output item list of an IO statement (a print statement) [directing output to the console] - inside that function you then also attempt to execute another IO statement [that outputs to the console]. This is not permitted - see 9.11p2 and p3 of F2003 or 9.12p2 of F2008.
A solution is to separate the function invocation from the io statement in the main program, i.e.
REAL :: a_temporary
...
a_temporary = plgndr(1,2,0.1)
PRINT *, a_temporary
Other alternatives in F2008 (but not F2003 - hence the [ ] parts in the first paragraph) include directing the output from the function to a different logical unit (note that WRITE (*, ... and PRINT ... reference the same unit).
In F2008 you could also replace the WRITE statement with a STOP statement with a message (the message must be a constant - which wouldn't let you report the problematic values).
The potential for inadvertently invoking recursive IO is part of the reason that some programming styles discourage conducting IO in functions.
Try:
if (m.lt.0.or.m.gt.l.or.abs(x).gt.1) then
write (*, *) "bad arguments to plgndr, aborting", m, x
stop
else
...
end if
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)
I'm trying to learn Fortran (unfortunately a necessity for my research group) - one of the tasks I set myself was to package one of the necessary functions (Associated Legendre polynomials) from the Numerical Recipes book into a fortran 03 compliant module. The original program (f77) has some error handling in the form of the following:
if(m.lt.0.or.m.gt.1.or.abs(x).gt.1)pause 'bad arguments in plgndr'
Pause seems to have been deprecated since f77 as using this line gives me a compiling error, so I tried the following:
module sha_helper
implicit none
public :: plgndr, factorial!, ylm
contains
! numerical recipes Associated Legendre Polynomials rewritten for f03
function plgndr(l,m,x) result(res_plgndr)
integer, intent(in) :: l, m
real, intent(in) :: x
real :: res_plgndr, fact, pll, pmm, pmmp1, somx2
integer :: i,ll
if (m.lt.0.or.m.gt.l.or.abs(x).gt.1) then
write (*, *) "bad arguments to plgndr, aborting", m, x
res_plgndr=-10e6 !return a ridiculous value
else
pmm = 1.
if (m.gt.0) then
somx2 = sqrt((1.-x)*(1.+x))
fact = 1.
do i = 1, m
pmm = -pmm*fact*somx2
fact = fact+2
end do
end if
if (l.eq.m) then
res_plgndr = pmm
else
pmmp1 = x*(2*m+1)*pmm
if(l.eq.m+1) then
res_plgndr = pmmp1
else
do ll = m+2, l
pll = (x*(2*ll-1)*pmmp1-(ll+m-1)*pmm)/(ll-m)
pmm = pmmp1
pmmp1 = pll
end do
res_plgndr = pll
end if
end if
end if
end function plgndr
recursive function factorial(n) result(factorial_result)
integer, intent(in) :: n
integer, parameter :: RegInt_K = selected_int_kind(20) !should be enough for the factorials I am using
integer (kind = RegInt_K) :: factorial_result
if (n <= 0) then
factorial_result = 1
else
factorial_result = n * factorial(n-1)
end if
end function factorial
! function ylm(l,m,theta,phi) result(res_ylm)
! integer, intent(in) :: l, m
! real, intent(in) :: theta, phi
! real :: res_ylm, front_block
! real, parameter :: pi = 3.1415926536
! front_block = sqrt((2*l+1)*factorial(l-abs(m))/(4*pi*))
! end function ylm
end module sha_helper
The main code after the else works, but if I execute my main program and call the function with bad values, the program freezes before executing the print statement. I know that the print statement is the problem, as commenting it out allows the function to execute normally, returning -10e6 as the value. Ideally, I would like the program to crash after giving a user readable error message, as giving bad values to the plgndr function is a fatal error for the program. The function plgndr is being used by the program sha_lmc. Currently all this does is read some arrays and then print a value of plgndr for testing (early days). The function ylm in the module sha_helper is also not finished, hence it is commented out. The code compiles using gfortran sha_helper.f03 sha_lmc.f03 -o sha_lmc, and
gfortran --version
GNU Fortran (GCC) 4.8.2
!Spherical Harmonic Bayesian Analysis testbed for Lagrangian Dynamical Monte Carlo
program sha_analysis
use sha_helper
implicit none
!Analysis Parameters
integer, parameter :: harm_order = 6
integer, parameter :: harm_array_length = (harm_order+1)**2
real, parameter :: coeff_lo = -0.1, coeff_hi = 0.1, data_err = 0.01 !for now, data_err fixed rather than heirarchical
!Monte Carlo Parameters
integer, parameter :: run = 100000, burn = 50000, thin = 100
real, parameter :: L = 1.0, e = 1.0
!Variables needed by the program
integer :: points, r, h, p, counter = 1
real, dimension(:), allocatable :: x, y, z
real, dimension(harm_array_length) :: l_index_list, m_index_list
real, dimension(:,:), allocatable :: g_matrix
!Open the file, allocate the x,y,z arrays and read the file
open(1, file = 'Average_H_M_C_PcP_boschi_1200.xyz', status = 'old')
read(1,*) points
allocate(x(points))
allocate(y(points))
allocate(z(points))
print *, "Number of Points: ", points
readloop: do r = 1, points
read(1,*) x(r), y(r), z(r)
end do readloop
!Set up the forwards model
allocate(g_matrix(harm_array_length,points))
!Generate the l and m values of spherical harmonics
hloop: do h = 0, harm_order
ploop: do p = -h,h
l_index_list(counter) = h
m_index_list(counter) = p
counter = counter + 1
end do ploop
end do hloop
print *, plgndr(1,2,0.1)
!print *, ylm(1,1,0.1,0.1)
end program sha_analysis
Your program does what is known as recursive IO - the initial call to plgndr is in the output item list of an IO statement (a print statement) [directing output to the console] - inside that function you then also attempt to execute another IO statement [that outputs to the console]. This is not permitted - see 9.11p2 and p3 of F2003 or 9.12p2 of F2008.
A solution is to separate the function invocation from the io statement in the main program, i.e.
REAL :: a_temporary
...
a_temporary = plgndr(1,2,0.1)
PRINT *, a_temporary
Other alternatives in F2008 (but not F2003 - hence the [ ] parts in the first paragraph) include directing the output from the function to a different logical unit (note that WRITE (*, ... and PRINT ... reference the same unit).
In F2008 you could also replace the WRITE statement with a STOP statement with a message (the message must be a constant - which wouldn't let you report the problematic values).
The potential for inadvertently invoking recursive IO is part of the reason that some programming styles discourage conducting IO in functions.
Try:
if (m.lt.0.or.m.gt.l.or.abs(x).gt.1) then
write (*, *) "bad arguments to plgndr, aborting", m, x
stop
else
...
end if