Fortran: Reading unformatted data - fortran

I have an large txt-file which contains measurements an the longitude/latitude of different stations, like:
1 1 bwb02 52.38783333 13.40266667 LR 0.2
2 2 bwb04 52.53716667 13.22583333 P1 0
3 3 bwb05 52.55516667 13.202 SP 0
4 4 bwb07 52.48433333 13.4385 N1 0
I just want to read the 4,5 and 7 column and put them into a matrix, like:
do j = 1,n
read(12,200) latB,lonB,r
200 Format (12x,f11.9,1x,f11.9,4x,f5.3)
beo_data(j,1) = j
beo_data(j,2) = lonB
beo_data(j,3) = latB
beo_data(j,4) = r
end do
But my format isn't working and i get an error:
Fortran runtime error: Bad value during floating point read
The problem seems to be that the format of the longitude and latitude is changing.
Could you tell me how i can read and work with such a file?

Just read each line as a string using a descriptor and then you can read your data using a list-directed read (* instead of the format). Or you can use the list directed read directly, if there is no danger of reading from two lines at once.
integer :: tmp1,tmp2
character(5) :: tmp3
character(2) :: tmp4
real :: values(3)
open(12,file="data.txt")
do j = 1,4
read(12,*) tmp1, tmp2, tmp3, values(1), values(2), tmp4, values(3)
print *,values
end do
close(12)
end
prints
52.3878326 13.4026670 0.200000003
52.5371666 13.2258329 0.00000000
52.5551682 13.2019997 0.00000000
52.4843330 13.4385004 0.00000000
Using the fixed column format is useful only when the columns are really fixed.
You can also just use single character variable tmp instead of all those different ones.

Related

matrix diagonalization and basis change with geev

I want to diagonalize a matrix and then be able to do basis changes. The aim in the end is to do matrix exponentiation, with exp(A) = P.exp(D).P^{-1}.
I use sgeev to diagonalize A. If I am not mistaken (and I probably am since it's not working), sgeev gives me P in the vr matrix and P^{-1} is transpose(vl). The diagonal matrix can be reconstitute from the eigenvalues wr.
The problem is that when I try to verify the matrix transformation by computing P * D * P^{-1} it's not giving A back.
Here's my code:
integer :: i,n, info
real::norm
real, allocatable:: A(:,:), B(:,:), C(:,:),D(:,:)
real, allocatable:: wr(:), wi(:), vl(:, :), vr(:, :), work(:)
n=3
allocate(vr(n,n), vl(n,n), wr(n), wi(n), work(4*n))
allocate(A(n,n),B(n,n), C(n,n),D(n,n))
A(1,:)=(/1,0,1/)
A(2,:)=(/0,2,1/)
A(3,:)=(/0,3,1/)
call sgeev('V','V',n,A,n,wr,wi,vl,n,vr,n,work,size(work,1),info)
print*,'eigenvalues'
do i=1,n
print*,i,wr(i),wi(i)
enddo
D=0.0
D(1,1)=wr(1)
D(2,2)=wr(2)
D(3,3)=wr(3)
C = matmul(D,transpose(vl))
B = matmul(vr,C)
print*,'A'
do i=1, n
print*, B(i,:)
enddo
The printed result is:
eigenvalues
1 1.00000000 0.00000000
2 3.30277562 0.00000000
3 -0.302775621 0.00000000
A
0.688247263 0.160159975 0.764021933
0.00000000 1.66581571 0.817408621
0.00000000 2.45222616 0.848407149
A is not the original A, not even considering an eventual factor.
I guess I am somehow mistaken since I checked the eigenvectors by computing matmul(A,vr) = matmul(vr,D) and matmul(transpose(vl),A) = matmul(D, transpose(vl)), and it worked.
Where am I wrong?
The problem is that transpose(vl) is not the inverse of vr. The normalisation given by sgeev is that each eigenvector (each column of vl or vr) is individually normalised. This means that dot_product(vl(:,i), vr(:,j)) is zero if i/=j, but is in general <1 if i==j.
If you want to get P^{-1}, you need to scale each column of vl by a factor of 1/dot_product(vl(:,i),vr(:,i) before transposing it.

Very small number turns negative in Fortran

I'm doing a program in Fortran90 which do a sum from i=1 to i=n where nis given. The sum is sum_{i=1}^{i=n}1/(i*(i+1)*(i+2)). This sum converges to 0.25. This is the code:
PROGRAM main
INTEGER n(4)
DOUBLE PRECISION s(4)
INTEGER i
OPEN(11,FILE='input')
OPEN(12,FILE='output')
DO i=1,4
READ(11,*) n(i)
END DO
PRINT*,n
CALL suma(n,s)
PRINT*, s
END
SUBROUTINE suma(n,s)
INTEGER n(4),j,k
DOUBLE PRECISION s(4),add
s=0
DO k=1,4
DO j=1,n(k)
add=1./(j*(j+1)*(j+2))
s(k)=s(k)+add
END DO
END DO
END SUBROUTINE
input
178
1586
18232
142705
The output file is now empty, I need to code it. I'm just printing the results, which are:
0.249984481688 0.249999400246 0.248687836759 0.247565846142
The problem comes with the variable add. When j is bigger, add turns negative, and the sum doesn't converge well. How can I fix it?
The problem is an integer overflow. 142705142706142707 is a number that is too large for a 4-byte integer.
What happens then is that the number overflows and loops back to negative numbers.
As #albert said in his comment, one solution is to convert it to double precision every step of the way: ((1.d0/j) / (j+1)) / (j+2). That way, it is calculating with floating point values.
Another option would be to use 8-byte integers:
integer, parameter :: int64 = selected_int_kind(17)
integer(kind=int64) :: j
You should be very careful with your calculations, though. Finer is not always better. I recommend that you look at how floating point arithmetic is performed by a computer, and what issues this can create. See for example here on wikipedia.
This is likely a better way to achieve what you want. I did remove the IO. The output from the program is
% gfortran -o z a.f90 && ./z
178 0.249984481688392
1586 0.249999801599584
18232 0.249999998496064
142705 0.249999999975453
program main
implicit none ! Never write a program without this statement
integer, parameter :: knd = kind(1.d0) ! double precision kind
integer n(4)
real(knd) s(4)
integer i
n = [178, 1586, 18232, 142705]
call suma(n, s)
do i = 1, 4
print '(I6,F18.15)', n(i), s(i)
end do
contains
!
! Recursively, sum a(j+1) = j * a(j) / (j + 1)
!
subroutine suma(n, s)
integer, intent(in) :: n(4)
real(knd), intent(out) :: s(4)
real(knd) aj
integer j, k
s = 0
do k = 1, 4
aj = 1 / real(1 * 2 * 3, knd) ! a(1)
do j = 1, n(k)
s(k) = s(k) + aj
aj = j * aj / (j + 3)
end do
end do
end subroutine
end program main

Program that decodes a message in Fortran

How can I take a text file that first lists a 2x2 matrix that was used to encrypt a message, followed by one integer per line (total 32 lines) that encode a message? Basically, I want to create a program that will:
1) Read and invert the 2x2 encryption matrix
2) Write a subroutine for determinant function to help invert matrix
3) Decrypt by reading in two integers at a time and inserting them into a character string (chracter*32 = full string)
And finally, print the hidden message.
I am pretty new to Fortran (and programming in general) but here is what I have so far and would appreciate any help.
program Program2
implicit none
INTEGER, DIMENSION(2,2) :: M, M2, M3
INTEGER :: B(32)
INTEGER :: row,col,max_rows,max_cols, Det, i, n, a
max_rows = 2
max_cols = 2
open(11, file = 'Data3.txt')
DO row = 1,max_rows
READ(11,*) (M(row,col),col=1,max_cols)
END DO
!Finding the inverse of a 2x2 matrix and reassigning.
M2(1,1) = M(2,2)
M2(2,2) = M(1,1)
M2(1,2) = -M(1,2)
M2(2,1) = -M(2,1)
! Could not get determinant function to yield correct answer (calc by hand)
M3 = M2/-1
print*, M3
print*, Det
open(11, file = 'Data3.txt')
i = a
do i = 1, 16
read(11,*) n
print*, n
enddo
close(11)
end program Program2
! Determinant function for a 2x2 matrix
function Determinant(M2) result(Det)
implicit none
INTEGER, DIMENSION(2,2) :: M, M2, M3
INTEGER :: B(32)
INTEGER :: row,col,max_rows,max_cols, Det, i, n, a
Det = M2(1,1)*M2(2,2) - M2(1,2)*M2(2,1)
end function Determinant
Here is the text file (or just a copy of what it looks like):
3 11
2 7
1488
955
703
458
1379
887
1465
943
1196
764
1392
895
1433
922
1403
902
1372
876
1467
943
697
454
1518
975
1596
1026
1392
895
1536
988
726
473
First of all, to get your Determinant function working, it needs a type (see Function has no implicit type):
INTEGER :: Determinant
Edit:
No need to be referenced as external, as pointed out in the comments. Thanks.
Then it can be used and it works well:
Det = Determinant(M2)
print*, Det
prints -1.
Second, could you please provide more explanation about what you want to do in step 3) so that we can help you out.
3) Decrypt by reading in two integers at a time and inserting them into a character string (chracter*32 = full string)

Discrete Fourier Transform seems to be printing incorrect answers?

I am attempting to write a program that calculates the discrete fourier transform of a set of given data. I've sampled a sine wave, so my set is (pi/2,2*pi,3*pi/2,2*pi). Here is my program:
program DFT
implicit none
integer :: k, N, x, y, j, r, l
integer, parameter :: dp = selected_real_kind(15,300)
real, allocatable,dimension(:) :: h, rst
integer, dimension(:,:), allocatable :: W
real(kind=dp) :: pi
open(unit=100, file="dft.dat",status='replace')
N = 4
allocate(h(N))
allocate(rst(N))
allocate(W(-N/2:N/2,1:N))
pi = 3.14159265359
do k=1,N
h(k) = k*(pi*0.5)
end do
do j = -N/2,N/2
do k = 1, N
W(j,k) = EXP((2.0_dp*pi*cmplx(0.0_dp,1.0_dp)*j*k)/N)
end do
end do
rst = matmul(W,h)
!print *, h, w
write(100,*) rst
end program
And this prints out the array rst as:
0.00000000 0.00000000 15.7079639 0.00000000 0.00000000
Using an online calculator, the results should be:
15.7+0j -3.14+3.14j -3.14+0j -3.14-3.14j
I'm not sure why rst is 1 entry too long either.
Can anyone spot why it's printing out 0 for 3/4 of the results? I notice that 15.7 appears in both the actual answers and my result.
Thank you
Even though the question has been answered and accepted, the program given has so many problems that I had to say...
The input given is not a sine wave, it's a linear function of time. Kind of like a 1-based ramp input.
For DFTs the indices normally are considered to go from 0:N-1, not 1:N.
For W the Nyquist frequency is represented twice, as -N/2 and N/2. Again it would have been normal to number the rows 0:N-1, BTW, this is why you have an extra output in your rst vector.
pi is double precision but only initialized to 12 significant figures. It's hard to tell if there's a typo in your value of pi which is why many would use 4*atan(1.0_dp) or acos(-1.0_dp).
Notice that h(N) is actually going to end up as the zero time input, which is one reason the whole world indices DFT vectors from zero.
The expression cmplx(0.0_dp,1.0_dp) is sort of futile because the CMPLX intrinsic always returns a single precision result if the third optional KIND= argument is not present. As a complex literal, (0.0_dp,1.0_dp) would be double precision. However, you could as well use (0,1) because it's exactly representable in single precision and would be converted to double precision when it gets multiplied by the growing product on its left. Also 2.0_dp could have been represented successfully as 2 with less clutter.
The expression EXP((2.0_dp*pi*cmplx(0.0_dp,1.0_dp)*j*k)/N) is appropriate for inverse DFT, disregarding normalization. Thus I would have written the whole thing more cleanly and correctly as EXP(-2*pi*(0,1)*j*k/N). Then the output should have been directly comparable to what the online calculator printed out.
Fortran does complex numbers for you but you must declare the appropriate variables as complex. Try
complex, allocatable,dimension(:) :: rst
complex, dimension(:,:), allocatable :: W

Reading and writing binary files (GrADS) in fortran

I am running a model and writing the model output to a binary file (a GrADS *gra file) such that, for example:
integer,parameter :: nvar =3 ,& !number of variables to be written to file
nx=10,ny=10,& !number of girdboxes in lat & long
nt = 5
integer :: it, & ! loop counter
irec ! Record number
real :: var1(nx,ny), var2(nx,ny),var3(nx,ny)
OPEN(30,file='Outfile.gra',action='write',form='unformatted',access='direct',&
recl=4*nVar*nx*ny,status='replace')
!loop over timesteps
it = 1, nt
irec = irec + 1
WRITE(1,rec=irec) Var1(:,:),Var2(:,:),Var3(:,:)
enddo
The file can be read in GrADS and the *ctl file looks like this
dset /mypath/Outfile.gra
title MyTitle
options little_endian
xdef 10 linear 1 1
ydef 10 linear 1 1
zdef 1 linear 1.0 1.0
tdef 5 linear 00:00Z01jan2012 1hr
vars 3
var1
var2
var3
endvars
What I would like to do, from a separate program, is write all the x&y of 1 variable at 1 timestep to a text file. I tried a number of ways but nothing has worked. My latest attempt is this one:
integer,parameter :: &
t = 3, & !Timestep I want to write to file
field = 2, & !Variable I want to write to file
nvar =3 , & !number of variables to be written to file
nx=10,ny=10, & !number of girdboxes in lat & long
nt = 5 !number of timesteps
inteǵer :: it,ix,iy,& ! loop counters
irec ! Record number
real :: val(nx,ny) ! Data to be written to file
open(1,file='NewFile.txt',status='replace')
open(2,file='Outfile.gra',action='read',form='unformatted',access='direct',&
recl=4*nVar*nx*ny,status='old')
irec = 0
do it = 1,nt
irec=irec + nvar*nx*ny
if(it == t) then
irec = irec + (field-1)*nx*ny
do ix = 1,nx
do iy = 1,ny
irec=irec+1
read(2,rec=irec) val(ix,iy)
enddo
enddo
write(1,*) val(:,:)
This particular example gives me the following error
Fortran runtime error: Non-existing record number
but I tried other variations that did not give me any errors but simply didn't write what I was trying to write to file. Could somebody tell me what I am doing wrong and how to solve this? Thank you.
OK, let me try again. When you write outfile.gra you appear to write nt records to it in the block
!loop over timesteps
it = 1, nt
irec = irec + 1
WRITE(1,rec=irec) Var1(:,:),Var2(:,:),Var3(:,:)
enddo
I guess that irec is initialised to 0 somewhere in the code. nt is set to 5 so, if my guess is correct, your code writes 5 records to outfile.gra.
Later, you read the same file in this block
irec = 0
do it = 1,nt
irec=irec + nvar*nx*ny
if(it == t) then
irec = irec + (field-1)*nx*ny
do ix = 1,nx
do iy = 1,ny
irec=irec+1
read(2,rec=irec) val(ix,iy)
enddo
enddo
It's unclear where the if statement closes but from your question I guess that it closes after the loops over nx and ny, like this:
irec = 0
do it = 1,nt
irec=irec + nvar*nx*ny
if(it == t) then
irec = irec + (field-1)*nx*ny
do ix = 1,nx
do iy = 1,ny
irec=irec+1
read(2,rec=irec) val(ix,iy)
enddo
enddo
end if
Again, if my guess is correct, then irec has the value 401 when the read statement is first executed.
It seems that you have written 5 records to outfile.gra and are trying to read the 401st record from it. It's entirely reasonable for the run-time to report that you are trying to read a record that doesn't exist.
High Performance Mark and hristo-iliev identified the problem with the record number. In order to be able to do what I want, i.e. write a single variable at a single timestep the right code is
irec = 0
do it = 1, t
irec = irec + 1
read(2,rec=irec) val(:,:,:)
enddo
write(1,*) val(:,:,field)
where val(nx,ny,nVar)