Fortran read from fie into arrays - fortran

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.

Related

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

How do I extract a floating number in the middle of an alphanumeric chain?

I am coding in Fortran 95.
I must extract the two floating values from this line in an ascii file:
circle(4097.0438,4111.337)
Of course the READ statement is
read(unit=11, fmt="(tr7,f9.4,tr1,f8.3)") x, y
The problem is that I must do that for hundreds of ascii files, but there are variations in the number of digits. For instance:
circle(201.043,7250.1234) # which would require fmt="(tr7,f7.3,tr1,f9.4)"
circle(0.004038,9999.12) # which would require fmt="(tr7,f8.6,tr1,f7.2)"
circle(0.004038,22.1234) # etc
Therefore I cannot use a single format. I don't know how to read those x, y coordinates using free format because I must skip the first 7 spaces anyway.
Reading in the data into a string buffer and removing unnecessary parts probably work.
program main
character(100) :: buf
real :: x, y
read( *, "(a)" ) buf
buf = buf( 8 : len(trim(buf))-1 ) !! remove "circle(" and ")"
read( buf, * ) x, y !! read two reals (commas/spaces are regarded as separators)
print *, x, y
end
Another way is to use index() to find "(" and ") [please see ref also]. In this case the data may contain any string before "(" or after ")". Similar methods can be used to extract necessary parts in more complicated cases.
integer :: n1, n2
n1 = index( buf, "(" )
n2 = index( buf, ")" )
read( buf( n1+1 : n2-1 ), * ) x, y
To supplement roygvib's answer I'll add a "cute" something. This takes the suggested approach of using list-directed input from an internal file.
program main
character(100) :: buf
complex :: temp
real :: x, y
read( *, "(a)" ) buf
read(buf(7:), *, decimal='point') temp ! read a complex
x = temp%RE ! assign x as real part of that
y = temp%IM ! assign y as imaginary part
print *, x, y
end
[Instead of temp%RE, temp%IM, possibly REAL(temp), AIMAG(temp).]
This works because we know that for a complex number with list-directed input (Fortran 2008, 10.10.3)
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.
I made the decimal='point' explicit in the code above. I also didn't remove the prefix from the buffer character variable itself.
Yes, this offers very little except less worry about removing the parantheses.

Simple read function error

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 )

Count the numbers of equal rows in a file

Suppose you have a file.dat of the form:
1
1
1
2
2
3
3
3
3
...
I want to count how many equal numbers there are and save them iteratively in a string. For instance:
m = 3 (times 1),
m = 2 (times 2),
m = 4 (times 3).
I put here my code:
program sele
implicit none
integer::j,k,s,n,l,r,m
real*8,allocatable::ID(:)
real*8:: j_r8,i_r8
open(10,file='data.dat')
n=0
DO
READ(10,*,END=100)
n=n+1
END DO
100 continue
rewind(10)
allocate(ID(n))
s=0
do s=1, n
read(10,*) ID(s)
end do
do r=1,n-1
if (ID(r)-ID(r+1) .EQ. 0) then
m = m + 1
print*, m
end if
end do
end program
The last do is the condition I'd like to expand, with something like:
if (condition is true) then
save an index of the number of equal digits
use this to do some operations:
do i = 1, number of equal digits
if (condition is not true) then
restart with the other digits.
If the values you want to read are integer values in a given limited range (for instance from 1 to 100), then the simplest way is the following :
program sele
implicit none
integer, parameter :: vmin=1
integer, parameter :: vmax=100
integer :: list(vmin:vmax)
integer :: value,i
open(10,file='data.dat')
list=0
do
read(10,*,end=10) value
if(value < vmin .OR. value > vmax) then
write(*,*) 'invalid value ',value
stop
endif
list(value)=list(value)+1
enddo
10 continue
do i=vmin,vmax
if(list(i) > 0) then
write(*,*) list(i),' times ',i
endif
enddo
end program
Which gives on your example :
3 times 1
2 times 2
4 times 3
It is possible to improve easily that program to manage variable vmin and vmax (the array list must then be declared allocatable and allocated at the right size).
If the range is too large, then a simple array is not accurate anymore and the right algorithm becomes more complicated : it must avoid to store unused values.

least squares minimisation fortran 77

Trying to get a one-parameter least squares minimisation working in fortran77. Here's the code; it compiles and seems to work except....it gets caught in an infinite loop between values of h1= 1.8E-2 and 3.5E-2.
Having a look now but, odds are, I'm not going to have much luck sussing the issue on my own. All help welcome!
PROGRAM assignment
! A program designed to fit experiemental data, using the method
! of least squares to minimise the associated chi-squared and
! obtain the four control parameters A,B,h1 and h2.
!*****************************************************************
IMPLICIT NONE
INTEGER i
DOUBLE PRECISION t(17),Ct(17),eCt(17)
DOUBLE PRECISION h1loop1,h1loop2,deltah,Cs
DOUBLE PRECISION chisqa,chisqb,dchisq
OPEN(21, FILE='data.txt', FORM='FORMATTED', STATUS='OLD')
DO i=1,17
READ(21,*)t(i),Ct(i),eCt(i)
END DO
CLOSE(21)
!Read in data.txt as three one dimensional arrays.
!*****************************************************************
!OPEN(21, FILE='outtest.txt', FORM='FORMATTED', STATUS='NEW')
!DO i=1,17
! WRITE(21,*)t(i),Ct(i),eCt(i)
!END DO
!CLOSE(21)
!
!Just to check input file is being read correctly.
!*****************************************************************
!**********************Minimising Lamda1 (h1)*********************
deltah= 0.0001
h1loop2= 0.001
h1loop1= 0.0 !Use initial value of 0 to calculate start-point chisq
DO 10
chisqa= 0.0
DO 20 i= 1, 17
Cs= exp(-h1loop1*t(i))
chisqa= chisqa + ((Ct(i) - Cs)/eCt(i))**2
20 END DO
chisqb= 0.0
DO 30 i= 1, 17
h1loop2= h1loop2 + deltah
Cs= exp(-h1loop2*t(i))
chisqb= chisqb + ((Ct(i) - Cs)/eCt(i))**2
30 END DO
!Print the two calculated chisq values to screen.
WRITE(6,*) 'Chi-squared a=',chisqa,'for Lamda1=',h1loop1
WRITE(6,*) 'Chi-squared b=',chisqb,'for Lamda1=',h1loop2
dchisq= chisqa - chisqb
IF (dchisq.GT.0.0) THEN
h1loop1= h1loop2
ELSE
deltah= deltah - ((deltah*2)/100)
END IF
IF (chisqb.LE.6618.681) EXIT
10 END DO
WRITE(6,*) 'Chi-squared is', chisqb,' for Lamda1 = ', h1loop2
END PROGRAM assignment
EDIT: Having looked at it again I've decided I have no clue what's screwing it up. Should be getting a chi-squared of 6618.681 from this, but it's just stuck between 6921.866 and 6920.031. Help!
do i=1
is not starting a loop, for a loop you need to specify an upper bound as well:
do i=1,ub
that's why you get the error message about the doi not having a type, in fixed format spaces are insignificant...
Edit: If you want to have an infinite loop, just skip the "i=" declaration completely. You can use an exit statement to leave the loop, when a certain criterion has been reached:
do
if (min_reached) EXIT
end do
Edit2: I don't know why you stick to F77 fixed format. Here is your program in free format, with some fixes of places, which looked weird, without digging too much into the details:
PROGRAM assignment
! A program designed to fit experiemental data, using the method
! of least squares to minimise the associated chi-squared and
! obtain the four control parameters A,B,h1 and h2.
!*****************************************************************
IMPLICIT NONE
integer, parameter :: rk = selected_real_kind(15)
integer, parameter :: nd = 17
integer :: i,t0
real(kind=rk) :: t(nd),t2(nd),Ct(nd),eCt(nd),Ctdiff(nd),c(nd)
real(kind=rk) :: Aa,Ab,Ba,Bb,h1a,h1b,h2a,h2b,chisqa,chisqb,dchisq
real(kind=rk) :: deltah,Cs(nd)
OPEN(21, FILE='data.txt', FORM='FORMATTED', STATUS='OLD')
DO i=1,nd
READ(21,*) t(i),Ct(i),eCt(i)
END DO
CLOSE(21)
!Read in data.txt as three one dimensional arrays.
!*****************************************************************
!OPEN(21, FILE='outtest.txt', FORM='FORMATTED', STATUS='NEW')
!DO i=1,17
! WRITE(21,*)t(i),Ct(i),eCt(i)
!END DO
!CLOSE(21)
!
!Just to check input file is being read correctly.
!*****************************************************************
!****************************Parameters***************************
Aa= 0
Ba= 0
h1a= 0
h2a= 0
!**********************Minimising Lamda1 (h1)*********************
deltah= 0.001_rk
h1b= deltah
minloop: DO
chisqa= 0
DO i= 1,nd
Cs(i)= exp(-h1a*t(i))!*Aa !+ Ba*exp(-h2a*t(i))
Ctdiff(i)= Ct(i) - Cs(i)
c(i)= Ctdiff(i)**2/eCt(i)**2
chisqa= chisqa + c(i)
h1a= h1a + deltah
END DO
! Use initial h1 value of 0 to calculate start-point chisq.
chisqb= 0
DO i= 1,nd
h1b= h1b + deltah
Cs(i)= exp(-h1b*t(i))!*Ab !+ Bb*exp(-h2b*t(i))
Ctdiff(i)= Ct(i) - Cs(i)
c(i)= Ctdiff(i)**2/eCt(i)**2
chisqb= chisqb + c(i)
END DO
! First-step h1 used to find competing chisq for comparison.
WRITE(6,*) 'Chi-squared a=', chisqa,'for Lamda1=',h1a
WRITE(6,*) 'Chi-squared b=', chisqb,'for Lamda1=',h1b
! Prints the two calculated chisq values to screen.
dchisq= chisqa - chisqb
IF (dchisq.GT.0) THEN
h1a= h1b
ELSE IF (dchisq.LE.0) THEN
deltah= (-deltah*2)/10
END IF
IF (chisqb.LE.6000) EXIT minloop
END DO minloop
WRITE(6,*) 'Chi-squared is', chisqb,'for Lamda1=',h1b
END PROGRAM assignment