Corrupted Fortran95 read with Multiple Data Type Input - file-io

After extended bug-hunting I've narrowed down that the issue appears to be coming from the input file, but my Professor and I have already tackled that avenue extensively, so the trouble might be my code.
The program (for now) is meant to read lines from a file format supporting multiple data types into a specially formatted array.
To test that functionality, I've made a temporary variable of the required type and printed out the result.
Here is my code:
program grading
implicit none
integer :: units = 16
integer :: uniti = 20
integer :: stuNum
character(12) :: fname0, fnam1
TYPE struct1
integer :: stNum
character(31) :: fname, Lname, MI(1)
character(22) :: locat
Integer :: phone
character(1) :: gender
integer :: classLevel
character(4) :: Major
integer :: credits
integer :: cmGPA
ENDTYPE struct1
TYPE(struct1), dimension(2) :: records
print *, "Enter the filename to be read"
read *, fname0
call fopen(units, fname0)
call populateType(units, records)
!***************************************************************
contains
subroutine fopen(units, fname0)
integer, intent(in) :: units
character(12), intent(in) :: fname0
integer :: test
open(unit=units, file=fname0, status="old", action="read", form = "formatted, iostat=test)
if(test /= 0) STOP "Error opening"
end subroutine fopen
!***************************************************************
subroutine populateType(units, records)
integer, intent(in) :: units
TYPE(struct1), dimension(2), intent(out) :: records
TYPE(struct1) :: store
integer :: test, loop
read (units,*, iostat = test)records
if(test > 0) then
write(*,*) "Something wrong with input"
else if (test < 0) then
write(*,*) "end of file reached"
else
print *, records
read *, records
endif
!read(units, 15, iostat=test)records
!if(test /= 0) stop "error reading"
!print *, records
!read *, records
15 format(I5,A31,A22,I7,A1,I1,A4,I2,I3)
close(units, status="keep")
end subroutine populateType
!***************************************************************
end program grading
Whenever I run the unformatted read without the iostat test, I get a bunch of zeros and integers that are inappropriately spaced ("Something wrong with input" when included).
Calling 'more' on the student.dat file to be read produces lines similar to the following:
10226FREUD FRED ELYNDEN, WASHINGTON 8340115M1PSYC 15185
10110PETERS ANDREW JLYNDEN, WASHINGTON 3239550M5ART 63205
10126ARISTOTLE ALICE ACHINO, CALIFORNIA 3330861F3PHIL 78310
10144LUCKY LUCY LGRANDVILLE, MICHIGAN 7745424F5HIST 66229
10284ORANGE DUTCH VGRAAFSCHAAP, MICHIGAN 3141660M2ENGR 42298
Also I'm working with a SMP Debian Linux OS x86_64
Originally we thought the problem was due to remote access corruption from a Mac, but the issue persists even when tested on the local workstations.
Any help figuring how to read without generating garbage values would be appreciated!

Related

Adding an extra dimension of size 1 and overhead

Veryreverie suggested an answer (https://stackoverflow.com/a/73366165/7462275) to merge two programs (a 2D and a 3D ones). The solution is to have only a 3D program and to set the third dimension to 1 for the the 2D case. I did not imagined this solution because I thought that there would be overhead by adding an useful third dimension. (Program is written in Fortran)
Then, I did many tests to compare 2D and 3D with third dimension = 1.
For example :
module m
implicit none
type :: int_3dim_t
integer :: x
integer :: y
integer :: z
end type int_3dim_t
end module m
program main
use m
implicit none
type(int_3dim_t) :: ii_curr, full_dim
real, dimension(:,:,:,:), allocatable :: myMatrix
real :: aaa
integer :: ll
open(unit=50,file='dim2.dat',status='old')
read(50,*) full_dim%x,full_dim%y
close(50)
full_dim%z = 1
allocate(myMatrix(1:8,1:full_dim%x,1:full_dim%y,1:full_dim%z))
associate (ii_x => ii_curr%x, ii_y => ii_curr%y, ii_z => ii_curr%z)
do ii_z = 1, full_dim%z
do ii_y = 1, full_dim%y
do ii_x = 1, full_dim%x
do ll=1,8
myMatrix(ll,ii_curr%x,ii_curr%y,ii_curr%z) = myMatrix(ll,ii_curr%x,ii_curr%y,ii_curr%z) + my_calc(ii_curr,ll)
enddo
enddo
enddo
enddo
end associate
print *,myMatrix(:,10,10,1)
contains
real function my_calc(ii,ll)
type(int_3dim_t), intent(in) :: ii
integer, intent(in) :: ll
my_calc = sqrt(real((ii%x/100.0)**2+(ii%y/100.0)**2))*ll
end function my_calc
end program main
The input file is 10000 10000 (another values do not change the conclusion), gfortran version is 12.1.1 and O2 optimisation flag is used.
(For 2D version, everything concerning the third dimension is removed)
And surprisingly, I did not found any difference in execution time.
So, I am looking for simple examples where this third dimension (set to 1) has an effect on the execution time. (Of course, with the correct fortran loop order : first indice in the innner loop and last indice in the outer loop)

passing a portion of an array of a derived type that contains an allocatable component

I'm having trouble when I pass a portion of an array of a derived type which has an allocatable component. A (probable) memory leaks seems to occur (unfortunatly valgrind is not yet available on mac 10.14/mojave, so I simply used a long do loop and looked the memory used with top).
Consider the following test program which reproduces my problem in a simpler context:
program test_leak
implicit none
type :: vint_t
integer, allocatable :: col(:)
end type vint_t
type(vint_t) :: row(4)
integer :: i, Ind(2)=[2,3]
do i = 1, 4
allocate(row(i)%col(i))
end do
row(1)%col(1) = 11
row(2)%col(1) = 21 ; row(2)%col(2) = 22
row(3)%col(1) = 31 ; row(3)%col(2) = 32 ; row(3)%col(3) = 33
row(4)%col(1) = 41 ; row(4)%col(2) = 42 ; row(4)%col(3) = 43 ; row(4)%col(4) = 44
do i = 1, 1000000
call do_nothing ( row(Ind) ) ! (version #A) with this version and with gfortran: memory grows with iter
!call do_nothing ( row(2:3) ) ! (version #B) but not with this version
if (mod(i,10000) == 0) then
print*,i ; read*
end if
end do
contains
subroutine do_nothing ( a )
type(vint_t), intent(in) :: a(:)
end subroutine do_nothing
end program test_leak
The problem occurs with gfortran (8.2) and not with ifort (19) nor with nagfor (6.2) (with nagfor and ifort the used memory is stable during the iterations and ca 400 Kb, while it reaches ca 30 Mb with gfortran!).
A final procedure doesn't solve the problem.
The problem disapears if the allocatable member "col" is replaced by an explicit array (say col(4)) or if the version #B is used.
The problem also occurs with an allocatable character component.
If I print "a" in the subroutine "do_nothing" both versions give the correct result.
Does anyone have some insight on this issue? Of course I can design the called subroutines by passing the array Ind (do_nothing(row, Ind)) but that would be somewhat a pity.

Logical Indexing based on "find" in Fortran 90

I am trying to make a logical array (B) to use in logical indexing based on values between .1 and .999 in an array (EP_G2) using a couple different methods 1) where loop 2) ANY.
program flux_3d
implicit none
INTEGER :: RMAX, YMAX, ZMAZ, timesteps
DOUBLE PRECISION, PARAMETER :: pmin=0.1
DOUBLE PRECISION, PARAMETER :: pmax=0.999
INTEGER :: sz
DOUBLE PRECISION, ALLOCATABLE :: EP_G2(:,:), C(:)
INTEGER, DIMENSION(RMAX*ZMAX*YMAX) :: B
LOGICAL, DIMENSION(RMAX*ZMAX*YMAX) :: A
! dimensions of array,
RMAX = 540
YMAX = 204
ZMAX = 54
timesteps = 1
!Open ascii array
OPEN(100, FILE ='EP_G2', form = 'formatted')
ALLOCATE( EP_G2(RMAX*ZMAX*YMAX, timesteps))
READ(100, *) EP_G2
WHERE(pmin<EP_G2(:,timesteps)<pmax) B = 1
ELSEWHERE
B = 0
ENDWHERE
PRINT*, B
! SZ # OF POINTS IN B
sz = count(B.eq.1)
!alternate way of finding points between pmin and pmax
A = ANY(pmin<EP_G2(:,t)<pmax)
print*, sz
!Then use the logical matrix B (or A) to make new array
!Basically I want a new array that isolates just the points in array between
!pmin and pmax
ALLOCATE(C(sz))
C = EP_G2(LOGICAL(B), 1)
The issue I am getting is that I either get the whole array or nothing and the command LOGICAL(B) gets an error that it isn't the right kind. I am new to Fortran coming from Matlab where I would just use find.
Additionally, since this array is over 5,948,640 x 1 computational time is an issue. I am using the Intel Fortran compiler (15.0 I believe).
Basically, I am looking for the fastest way to find the indexes of points in an array between two numbers.
Any help would be very appreciated.
I'm a little confused by your code and question but I think you want to find the indices of elements in an array whose values lie between 0.1 and 0.999. It's not entirely clear that you want the indices, or just the elements themselves, but bear with me and I'll explain how to get both.
Suppose your original array is declared something like
real, dimension(10**6) :: values
and that it is populated with values. Then I might declare an index array like this
integer, dimension(10**6) :: indices = [(ix,ix=1,10**6)]
(obviously you'll need to declare the variable ix too).
Now, the expression
pack(indices, values>pmin.and.values<pmax)
will return those elements of indices whose values lie strictly between pmin and pmax. Of course, if you only want the values themselves you can dispense with indices entirely and write
pack(values, values>pmin.and.values<pmax)
Both of these uses of pack will return an array of rank 1, and you can assign the returned array to an allocatable, Fortran will take care of the sizing for you.
I'll leave it to you to expand this approach to the rank-2 array you are actually working with, but it shouldn't be too difficult to do that. Ask for more help if you need it.
And is this the fastest approach ? I'm not sure, but it was very quick to write and if you're interested in its run-time performance I suggest you test that.
Incidentally, the Fortran intrinsic function logical is for transforming logical values from one kind to another, it's not defined on integers (or any other intrinsic type). Fortran predates the madness of regarding 0 and 1 as logical values.
How can the arrays be the same?
RMAX, YMAX, ZMAZ, and timesteps values are not known until after A, B, and C are declared - So they (A and B) will not likely be the size you want.
implicit none
INTEGER :: RMAX, YMAX, ZMAZ, timesteps
DOUBLE PRECISION, PARAMETER :: pmin=0.1
DOUBLE PRECISION, PARAMETER :: pmax=0.999
INTEGER :: sz
DOUBLE PRECISION, ALLOCATABLE :: EP_G2(:,:), C(:)
INTEGER, DIMENSION(RMAX*ZMAX*YMAX) :: B
LOGICAL, DIMENSION(RMAX*ZMAX*YMAX) :: A
! dimensions of array,
RMAX = 540
YMAX = 204
ZMAX = 54
timesteps = 1
You probably want either this:
implicit none
INTEGER , PARAMETER :: RMAX = 504
INTEGER , PARAMETER :: YMAX = 204
INTEGER , PARAMETER :: ZMAZ = 54
INTEGER , PARAMETER :: timesteps = 1
DOUBLE PRECISION, PARAMETER :: pmin = 0.1
DOUBLE PRECISION, PARAMETER :: pmax = 0.999
INTEGER :: sz
DOUBLE PRECISION, ALLOCATABLE :: EP_G2(:,:), C(:)
INTEGER, DIMENSION(RMAX*ZMAX*YMAX) :: B
LOGICAL, DIMENSION(RMAX*ZMAX*YMAX) :: A
! dimensions of array,
!RMAX = 540
!YMAX = 204
!ZMAX = 54
!timesteps = 1
Or this:
implicit none
! dimensions of array
INTEGER , PARAMETER :: RMAX = 504
INTEGER , PARAMETER :: YMAX = 204
INTEGER , PARAMETER :: ZMAZ = 54
INTEGER , PARAMETER :: timesteps = 1
DOUBLE PRECISION, PARAMETER :: pmin = 0.1
DOUBLE PRECISION, PARAMETER :: pmax = 0.999
INTEGER :: sz
DOUBLE PRECISION, ALLOCATABLE :: EP_G2(:,:), C(:)
INTEGER, DIMENSION(:), ALLOCATABLE :: B
LOGICAL, DIMENSION(:), ALLOCATABLE :: A
! Then allocate A and B
And you may also want to consider the use of SHAPE or SIZE to see if the rank and size of the array are correct.
IF(SHAPE(A) /= SHAPE(B) ) ... chuck and error message.
IF(SIZE(A,1) /= SIZE(B,1) ) etc

How do I read numbers from a file in Fortran and immediately do calculations with each numbers?

I'm totally new to Fortran, and I'm trying to learn the language here:
http://www.fortrantutorial.com/files-precision/index.php. I have some basic experience with C and Python, but not much, like introduction class and such.
So in exercises 4.1, they ask me to input some numbers from a file, and check if these numbers are even or odd. Here is the code to input the numbers:
program readdata
implicit none
!reads data from a file called mydata.txt
real :: x,y,z
open(10,file='mydata.txt')
read(10,*) x,y,z
print *,x,y,z
end program readdata
The file mydata.txt contains some random numbers. And they can check if the number is even or odd by:
if (mod(num,2)>0) then……
My question is that: if this file have like 10, or 1000 numbers, do I have to manually assign every single one of them? Is there any other way for me to do quick calculation with mass numbers situation like that?
Every read also moves the read pointer forwards. So with every new read, a new line is read in from the file.
The easiest thing to do is to keep reading until the READ statement returns an error. Of course, you have to pass a variable for the READ to write its error into. Something like this:
program readdata
implicit none
real :: x, y, z
integer :: iounit, ios
open(newunit=iounit, file='mydata.txt', iostat=ios, action='READ')
if (ios /= 0) STOP 1
do
read(iounit, *, iostat=ios) x, y, z
if (ios /= 0) exit
print *, x, y, z
end do
close(iounit)
end program readdata
Update: If you're limited to Fortran 95, as OP suggested in his comments, here's what to change: Instead of
integer :: iounit, ios
open(newunit=iounit, ...)
you use
integer :: ios
integer, paramter :: iounit = 100
open(unit=iounit, ...)
All that's important is that iounit is a number, greater than 10, which is not used as a unit for any other read/write operation.
well a mass of numbers means that X, Y, and Z need to be more than a single number of each... so something like this should get you vectored towards a solution:
program readdata
implicit none
!reads data from a file called mydata.txt
INTEGER, PARAMETER :: max2process = 10000
INTEGER :: I
INTEGER :: K = 0
real , DIMENSION(max2process) :: x,y,z
LOGICAL, DIMENSION(max2process) :: ODD = .FALSE.
open(10,file='mydata.txt')
10 CONTINUE
X(:) = 0
Y(:) = 0
K(:) = 0
DO I = 1, max2process
K = K + I
read(10,*, EOF=99) x(I), y(I), z(I)
print *,x(I), y(I), z(I)
ENDDO
WRITE(*,22) I, MINVAL(X(1:I)), MAXVAL(X(1:I)
22 FORMAT(' MIN(X(1:',I5,')=',0PE12.5,' Max=',0PE12.5)
odd = .FALSE.
WHERE (MODULO(X, 2) /= 0)
ODD = .TRUE.
ENDWHERE
DO I = 1, 10
WRITE(*,88) I, X(I), odd(I)
88 FORMAT('x(', I2,')=',0PE12.5,' odd="',L1,'"')
EBDDO
WRITE(*,*) ' we did not hit the end of file... Go back and read some more'
GOTO 10
99 WRITE(*,*) 'END of file reached at ', (K-1)
end program readdata
Basically something like that, but I probably have a typo (LTR). If MODULO is ELEMENTAL then you can do it easily... And MODULO is ELEMENTAL, so you are in luck (which is common).
There is a https://rosettacode.org/wiki.Even_or_Odd#Fortran that looks nice. the functions could be further enhanced using ELEMENTAL FUNCTION.
that should give you enough to work it out.
or you could read the file to find the size, close and reopen it, and then allocate X, Y, Z and Odd... the example i gave was more of a streaming example. one should generally use a DO WHILE rather than a GOTO, but starting out a GOTO can be conceptually easier. (You need an EXIT or some DO WHILE (IOSTAT /= ) in order to break out)

Reading a known number of variable from a file when one of the variables are missing in input file

I already checked similar posting. The solution is given by M. S. B. here Reading data file in Fortran with known number of lines but unknown number of entries in each line
So, the problem I am having is that from text file I am trying to read inputs. In one line there is supposed to be 3 variables. But sometimes the input file may have 2 variables. In that case I need to make the last variable zero. I tried using READ statement with IOSTAT but if there is only two values it goes to the next line and reads the next available value. I need to make it stop in the 1st line after reading 2 values when there is no 3rd value.
I found one way to do that is to have a comment/other than the type I am trying to read (in this case I am reading float while a comment is a char) which makes a IOSTAT>0 and I can use that as a check. But if in some cases I may not have that comment. I want to make sure it works even than.
Part of the code
read(15,*) x
read(15,*,IOSTAT=ioerr) y,z,w
if (ioerr.gt.0) then
write(*,*)'No value was found'
w=0.0;
goto 409
elseif (ioerr.eq.0) then
write(*,*)'Value found', w
endif
409 read(15,*) a,b
read(15,*) c,d
INPUT FILE is of the form
-1.000 abcd
12.460 28.000 8.00 efg
5.000 5.000 hijk
20.000 21.000 lmno
I need to make it work even when there is no "8.00 efg"
for this case
-1.000 abcd
12.460 28.000
5.000 5.000 hijk
20.000 21.000 lmno
I can not use the string method suggested by MSB. Is there any other way?
I seem to remember trying to do something similar in the past. If you know that the size of a line of the file won't exceed a certain number, you might be able to try something like:
...
character*(128) A
read(15,'(A128)') A !This now holds 1 line of text, padded on the right with spaces
read(A,*,IOSTAT=ioerror) x,y,z
if(IOSTAT.gt.0)then
!handle error here
endif
I'm not completely sure how portable this solution is from one compiler to the next and I don't have time right now to read up on it in the f77 standard...
I have a routine that counts the number of reals on a line. You could adapt this to your purpose fairly easily I think.
subroutine line_num_columns(iu,N,count)
implicit none
integer(4),intent(in)::iu,N
character(len=N)::line
real(8),allocatable::r(:)
integer(4)::r_size,count,i,j
count=0 !Set to zero in case of premature return
r_size=N/5 !Initially try out this max number of reals
allocate(r(r_size))
read(iu,'(a)') line
50 continue
do i=1,r_size
read(line,*,end=99) (r(j),j=1,i) !Try reading i reals
count=i
!write(*,*) count
enddo
r_size=r_size*2 !Need more reals
deallocate(r)
allocate(r(r_size))
goto 50
return
99 continue
write(*,*) 'I conclude that there are ',count,' reals on the first line'
end subroutine line_num_columns
If a Fortran 90 solution is fine, you can use the following procedure to parse a line with multiple real values:
subroutine readnext_r1(string, pos, value)
implicit none
character(len=*), intent(in) :: string
integer, intent(inout) :: pos
real, intent(out) :: value
integer :: i1, i2
i2 = len_trim(string)
! initial values:
if (pos > i2) then
pos = 0
value = 0.0
return
end if
! skip blanks:
i1 = pos
do
if (string(i1:i1) /= ' ') exit
i1 = i1 + 1
end do
! read real value and set pos:
read(string(i1:i2), *) value
pos = scan(string(i1:i2), ' ')
if (pos == 0) then
pos = i2 + 1
else
pos = pos + i1 - 1
end if
end subroutine readnext_r1
The subroutine reads the next real number from a string 'string' starting at character number 'pos' and returns the value in 'value'. If the end of the string has been reached, 'pos' is set to zero (and a value of 0.0 is returned), otherwise 'pos' is incremented to the character position behind the real number that was read.
So, for your case you would first read the line to a character string:
character(len=1024) :: line
...
read(15,'(A)') line
...
and then parse this string
real :: y, z, w
integer :: pos
...
pos = 1
call readnext_r1(line, pos, y)
call readnext_r1(line, pos, z)
call readnext_r1(line, pos, w)
if (pos == 0) w = 0.0
where the final 'if' is not even necessary (but this way it is more transparent imho).
Note, that this technique will fail if there is a third entry on the line that is not a real number.
I know the following simple solution:
w = 0.0
read(15,*,err=600)y, z, w
goto 610
600 read(15,*)y, z
610 do other stuff
But it contains "goto" operators
You might be able to use the wonderfully named colon edit descriptor. This allows you to skip the rest of a format if there are no further items in the I/O list:
Program test
Implicit None
Real :: a, b, c
Character( Len = 10 ) :: comment
Do
c = 0.0
comment = 'No comment'
Read( *, '( 2( f7.3, 1x ), :, f7.3, a )' ) a, b, c, comment
Write( *, * ) 'I read ', a, b, c, comment
End Do
End Program test
For instance with gfortran I get:
Wot now? gfortran -W -Wall -pedantic -std=f95 col.f90
Wot now? ./a.out
12.460 28.000 8.00 efg
I read 12.460000 28.000000 8.0000000 efg
12.460 28.000
I read 12.460000 28.000000 0.00000000E+00
^C
This works with gfortran, g95, the NAG compiler, Intel's compiler and the Sun/Oracle compiler. However I should say I'm not totally convinced I understand this - if c or comment are NOT read are they guaranteed to be 0 and all spaces respectively? Not sure, need to ask elsewhere.