Simple read function error - fortran

I imagine this is something silly I've missed but I've asked my whole class and noone can seem to work it out. Making a simple program calling in a subroutine and I'm having trouble with the do loop reading in the entries of the matrix.
program Householder_Program
use QR_Factorisation
use numeric_kinds
complex(dp), dimension(:,:), allocatable :: A, Q, R, V
integer :: i, j, n, m
print *, 'Enter how many rows in the matrix A'
read *, m
print *, 'Enter how many columns in the matrix A'
read *, n
allocate(A(m,n), Q(m,n), R(n,n), V(n,n))
do i = 1,m
do j = 1,n
Print *, 'Enter row', i, 'and column', j, 'of matrix A'
read *, A(i,j)
end do
end do
call Householder_Triangularization(A,V,R,n,m)
print *, R
end program
It will ask me for A(1,1) but when I type in a number it will not ask me for A(1,2), it will leave a blank line. When I try to put in a 2nd number it will error and say :
Enter row 1 and column 1 of matrix A
1
2
At line 22 of file HouseholderProgram.f90 (unit = 5, file = 'stdin')
Fortran runtime error: Bad repeat count in item 1 of list input

Your variable A is (an array) of type complex. This means that when you attempt to do the list-directed input of the element values you cannot just specify a single number. So, in your case the problem is not with the program but with the input.
From the Fortran 2008 standard, 10.10.3
When the next effective item is of type complex, the input form consists of a left parenthesis followed by an ordered pair of numeric input fields separated by a comma (if the decimal edit mode is POINT) or semicolon (if the decimal edit mode is COMMA), and followed by a right parenthesis.
Input, then, must be something like (1., 12.).

You are trying to read in complex numbers (A is complex)! As such, you should specify complex numbers to the code... Since you are providing just one integer, the program does not know what to do.
Providing (1,0) and (2,0) instead of 1 and 2 will do the trick.

In case the user input is always real, and you want to read it into a complex type array you can do something like this:
Print *, 'Enter row', i, 'and column', j, 'of matrix A'
read *, dummy
A(i,j)=dummy
where dummy is declared real. This will save the user from the need to key in the parenthesis required for complex numbers. ( The conversion to complex is automatic )

Related

Fortran element wise multiplication of a matrix and a vector

Is there a simple and quick way to multiply a column of a matrix with element of a vector. We can do this explicitly,
program test
integer :: x(3,3), y(3), z(3,3)
x = reshape([(i,i=1,9)],[3,3])
y = [1,2,3]
do i=1,3
z(:,i) = x(:,i) * y(i)
print *, z(:,i)
enddo
end program test
Is there a way to perform the do loop in one line. For example in Numpy python we can do this to do the job in one shot
z = np.einsum('ij,i->ij',x,y)
#or
z = x*y[:,None]
Try
z = x * spread(y,1,3)
and if that doesn't work (no Fortran on this computer so I haven't checked) fiddle around with spread until it does. In practice you'll probably want to replace the 3 by size(x,1) or suchlike.
I expect that this will cause the compiler to create temporary arrays. And I expect it will be easy to find situations where it underperforms the explicit looping scheme in the question. 'neat' one-liners often have a cost in both time and space. And often tried-and-trusted Fortran approach of explicit looping is the one to go with.
Why replace clear easy to read code with garbage?
program test
implicit none
integer i,j
integer :: x(3,3), y(3), z(3,3)
x = reshape([(i,i=1,9)],[3,3])
y = [1,2,3]
z = reshape ([((x(j,i)*y(i) ,j=1,3),i=1,3)], [3,3])
print *, z(1,:)
print *, z(2,:)
print *, z(3,:)
end program test

Reading an image into an array?

I'm attempting to write a program that utilizes the sobel filter to detect edges in images. So first off, I've written down some of the requirements in some basic code, such as the x and y direction filters as arrays and also an attempt to read in a pgm image:
program edges
implicit none
integer, dimension(:,:), allocatable :: inp, outim, GX, GY
integer, parameter :: dp = selected_real_kind(15,300)
integer :: ky, kx, x, y, out_unit = 10, M, N, sx, sy, i, j
real(kind=dp) :: G
M = 5
N = 5
allocate(inp(M,N))
allocate(outim(M-2,N-2))
allocate(GX(3,3))
allocate(GY(3,3))
open(file = 'clown.pgm',unit=out_unit,status= 'unknown') !opening file to write to inp
read (out_unit,11) 'P2' !pgm magic number
read (out_unit,12) 50,50 !width, height
read (out_unit,13) 1 !max gray value
do M=-25,25
do N=-25,25
read (out_unit,*) inp(M,N)
end do
end do
11 format(a2)
12 format(i3,1x,i3)
13 format(i5)
This is my first time working with image manipulation in FORTRAN, apart from once when I printed an image out as a pbm file. The code for reading the image in is a replicate of what I used to print one out before, except I changed write to read.
So my question is, how can I read in an image that's in pgm format into the "inp" array, so that I can apply the sobel filter? When I run my attempt, I get the following errors:
read (out_unit,11) 'P2' !pgm magic number
1
Error: Expected variable in READ statement at (1)
sobel.f90:41:18:
read (out_unit,12) 50,50 !width, height
1
Error: Expected variable in READ statement at (1)
sobel.f90:42:18:
read (out_unit,13) 1 !max gray value
1
Error: Expected variable in READ statement at (1)
Thank you
The first mistake is, as the compiler states quite clearly, in this line:
read (out_unit,11) 'P2'
The compiler expects, because that's the way that Fortran is defined, to be told to read a value from out_unit into a variable, but 'P2' is a character literal (or whatever the heck the standard calls them), it's a string, it's not a variable.
The same mistake occurs in the next lines too. The compiler expects something like
integer :: p2 ! pgm magic number
...
read (out_unit,11) p2
etc. After execution of this version of the read statement the variable p2 holds the magic number read from the pgm file.
While I'm writing, calling the unit to be read from out_unit is just perverse and will, eventually, confuse you.

Read signed exponential

I am having trouble reading exponential from a text file using Fortran.
The entry in the text file looks like the following
0.02547163e+06-0.04601176e+01 0.02500000e+02 0.00000000e+00 0.00000000e+00 3
And the code that I am using looks like the following
read(iunit,'(ES20.8,ES20.8,ES20.8,ES20.8,ES20.8,I2)') dummy1, dummy2, Thermo_DB_Coeffs_LowT(iS,1:3),temp
The error I am getting is
Fortran runtime error: Bad value during floating point read
How can I read these values?
Well here is what I usually do when it is too painful to hand edit the file...
CHARACTER(LEN=256) :: Line
INTEGER, PARAMETER :: Start = 1
INTEGER :: Fin, Trailing_Int, I
DOUBLE, DIMENSION(6) :: Element
...
Ingest_All_Rows: DO WHILE(.TRUE.)
READ(...) Line ! Into a character-string
<if we get to the end of the file, then 'EXIT Ingest_All_Rows'>
Start =1
Single_Row_Ingest: DO I = 1, 6
Fin = SCAN(Line,'eE')+3 !or maybe it is 4?
IF(I ==6) Fin = LEN_TRIM(Line)
READ(Line(Start:Fin),*) Element(I) !fron the string(len-string) to the double.
Line = Line((Fin+1):)
IF(I ==6) Trailing_Int = Element(6)
ENDDO Single_Row_Ingest
<Here we shove the row's 5 elements into some array, and the trailing int somewhere>
ENDDO Ingest_All_Rows
You will have to fill in the blanks, but I find that SCAN and LEN_TRIM can be useful in these cases

Fortran read from fie into arrays

I need to read from a txt file into three arrays in order to calculate distances between coordinates. I have looked through other answers and pieced together the script bellow. The columns of the input file need to be come my x, y and z arrays respectively. There are 64 rows and 16 decimal places for each entry. Pointers would be greatly appreciated.
the data format:
0.8607595188703266 0.9252035918116585 0.4094258340665792
0.5246038490998378 0.9804633529144733 0.5325820695466118
0.6955271184611949 0.3304908806613460 0.7154502550542654
and my script so far:
PROGRAM readtest
use, intrinsic :: iso_fortran_env
implicit none
integer, parameter :: ArrayLen = 64
real(real64), DIMENSION(ArrayLen) :: x
real(real64), DIMENSION(ArrayLen) :: y
real(real64), DIMENSION(ArrayLen) :: z
integer :: i, ReadCode, num
OPEN(1, FILE='contcar.txt', STATUS='old', ACTION='read')
OPEN(2, FILE='xyz.txt', STATUS='replace', ACTION='write')
num = 0
ReadLoop: do i=1, ArrayLen
read (1, '(A,F18.16)', iostat=ReadCode ) x(i), y(i), z(i)
if ( ReadCode /= 0 ) then
if ( ReadCode == iostat_end ) then
exit ReadLoop
else
write ( *, '( / "Error on read: ", I0 )' ) ReadCode
stop
end if
end if
num = num + 1
end do ReadLoop
WRITE(3, 100) x, y, z
100 format (A,F18.16)
END PROGRAM readtest
The xyz.txt is appearing blank and I am not getting any errors at this stage, what is wrong here that is stopping the array filling and writing to the file?
Sorry, if this is too much of mess to approach, any help would be appreciated.
Thanks
You have two problems with you approach:
Your format specifications are wrong
Your write won't do what you want
First off, the format A,F18.16 reads two items, a character and a floating point number. What you want is to read 3 floating point numbers. With the file provided, there are two spaces before each number so you could use
read (1, '(3(2X,F18.16))', iostat=ReadCode ) x(i), y(i), z(i)
but this is not very flexible if your input format changes and it is easier to just do list-directed input:
read (1, *, iostat=ReadCode ) x(i), y(i), z(i)
which will do what you want and is not sensitive to the exact positioning of the numbers within the file and how many intervening spaces exist.
This will load your data into the arrays. Now for output you want the same thing. You want to duplicate the output so we can use the first format about to stipulate that output (3(2X,F18.16)). This will output 3 numbers per line with 2 spaces before each number. The next problem is that you are attempting
WRITE(3, 100) x, y, z
which will transpose your array. It will write all of the x, then all of the y and lastly all of the z. If you want the same output, put it in a loop. Putting the above together, use:
do i=1, ArrayLen
WRITE(2, 100) x(i), y(i), z(i)
end do
100 format (3(2X,F18.16))
As a note, don't use single digit unit numbers, particularly the first few which conflict with system defined standard input/output/error units on most compilers.

How to find statistical mode in Fortran

I'm trying to write a program to find the mean, median, mode of an integer array but am having some complications in finding the mode. The following is the code that I've written so far.
First, the program will prompt user to enter a value for the number of integers that will be entered followed by request to enter that number of integers. The integers are then sorted in ascending order and the mean and median are found.
The problem I am having is when I try to get the mode. I am able to count the number of occurrence of a repetitive value. By finding the value with highest occurrence, we'll be able to find Mode. But I am unsure how to do this. Is there any intrinsic function in Fortran to calculate number of occurrence of input values and the value with highest occurrence?
PROGRAM STATISTICS
!Created by : Rethnaraj Rambabu
IMPLICIT NONE
REAL, DIMENSION(:), ALLOCATABLE:: VAL
REAL TEMP, MEDIAN
REAL EVEN, MEAN, SUM, FMODE
INTEGER N, I,J
WRITE(*,*)' WHAT IS THE VALUE FOR N? '
READ(*,*) N
ALLOCATE(VAL(N))
WRITE(*,*) 'ENTER THE NUMBERS'
OPEN(1,FILE='FILE.TXT')
READ(1,*)(VAL(I),I=1,N)
CLOSE(1)
WRITE(*,*) VAL
!/---FOR SORTING----/!
DO I=1,N-1
DO J=1,N-1
IF(VAL(J) > VAL(J+1)) THEN
TEMP=VAL(J)
VAL(J)=VAL(J+1)
VAL(J+1)=TEMP
END IF
END DO
END DO
WRITE(*,*) VAL
!/-----MEDIAN----/!
IF ((N/2*2) /= N) THEN
MEDIAN=VAL((N+1)/2)
ELSE IF ((N/2*2) == N) THEN
EVEN= (VAL(N/2)+VAL((N+2)/2))
MEDIAN=EVEN/2
END IF
WRITE(*,*)'MEDIAN=', MEDIAN
!/----MEAN----/
SUM=0
DO I=1,N
SUM=SUM+VAL(I)
END DO
MEAN=SUM/N
WRITE(*,*)'MEAN=', MEAN
!/------MODE----/
FMODE=1
DO I=1,N-1
IF (VAL(I) == VAL(I+1)) THEN
FMODE=FMODE+1
END IF
END DO
WRITE(*,*)FMODE
END PROGRAM
The FILE.TXT contains
10 8 1 9 8 9 9 7 5 9 3 5 6
But, how to do that? Or is there any intrinsic function in Fortran to calculate number of occurrence of input values and the value with highest occurrence.
No, there is not. You'll have to calculate the mode by hand.
The following code should work (on a sorted array):
FMODE = VAL(1)
COUNT = 1
CURRENTCOUNT = 1
DO I = 2, N
! We are going through the loop looking for values == VAL(I-1)...
IF (VAL(I) == VAL(I-1)) THEN
! We spotted another VAL(I-1), so increment the count.
CURRENTCOUNT = CURRENTCOUNT + 1
ELSE
! There are no more VAL(I-1)
IF (CURRENTCOUNT > COUNT) THEN
! There were more elements of value VAL(I-1) than of value FMODE
COUNT = CURRENTCOUNT
FMODE = VAL(I-1)
END IF
! Next we are looking for values == VAL(I), so far we have spotted one...
CURRENTCOUNT = 1
END
END DO
IF (CURRENTCOUNT > COUNT) THEN
! This means there are more elements of value VAL(N) than of value FMODE.
FMODE = VAL(N)
END IF
Explanation:
We keep the best-so-far mode in the FMODE variable, and the count of the FMODE in the COUNT variable. As we step through the array we count the number of hits that are equal to what we are looking at now, in the CURRENTCOUNT variable.
If the next item we look at is equal to the previous, we simply increment the CURRENTCOUNT. If it's different, then we need to reset the CURRENTCOUNT, because we will now count the number of duplications of the next element.
Before we reset the CURRENTCOUNT we check if it's bigger than the previous best result, and if it is, we overwrite the previous best result (the FMODE and COUNT variables) with the new best results (whatever is at VAL(I) and CURRENTCOUNT), before we continue.
This reset doesn't happen at the end of the loop, so I inserted another check at the end in case the most frequent element happens to be the final element of the loop. In that case we overwrite FMODE, like we would have done in the loop.
It is a bit lengthy, you could probably get rid of the optional argument, but there is an example provided here. They use the quick sort algorithm as implemented here.
Alternatively, you could use
integer function mode(arr) result(m)
implicit none
integer, dimension(:), intent(in) :: arr
! Local variables
integer, dimension(:), allocatable :: counts
integer :: i, astat
character(len=128) :: error_str
! Initialise array to count occurrences of each value.
allocate(counts(minval(arr):maxval(arr)), stat=astat, errmsg=error_str)
if (astat/=0) then
print'("Allocation of counts array failed.")'
print*, error_str
end if
counts = 0
! Loop over inputted array, counting occurrence of each value.
do i=1,size(arr)
counts(arr(i)) = counts(arr(i)) + 1
end do
! Finally, find the mode
m = minloc(abs(counts - maxval(counts)),1)
end function mode
This doesn't require any sorting.