I have a file.txt such as follow:
1. 0. 3.21
1. 1. 2.11
1. 2. 1.554
1. 0. 3.21
1. 3. 1.111
1. 2. 1.554
As you can see I have two lines that are equal one to another (the first and the fourth and the third and the sixth). My attempt is to eliminate the lines that are equals to obtain something like:
1. 0. 3.21
1. 1. 2.11
1. 2. 1.554
1. 3. 1.111
My try of a fortran program to do it is:
program mean
implicit none
integer :: i,j,n,s,units
REAL*8,allocatable:: x(:),y(:),amp(:)
! open the file I want to change
OPEN(UNIT=10,FILE='oldfile.dat')
n=0
DO
READ(10,*,END=100)
n=n+1
END DO
100 continue
rewind(10)
allocate(x(n),y(n),amp(n))
s=0
! save the numbers from the file in three different vectors
do s=1, n
read(10,*) x(s), y(s),amp(s)
end do
!---------------------!
! Open the file that should contains the new data without repetition
units=107
open(unit=units,file='newfile.dat')
! THIS SHOULD WRITE ONLY NOT EQUAL ELEMENTS of THE oldfile.dat:
! scan the elements in the third column and write only the elements for which
! the if statement is true, namely: write only the elements (x,y,amp) that have
! different values in the third column.
do i=1,n
do j = i+1,n
if (amp(i) .ne. amp(j)) then !
write(units,*),x(j),y(j),amp(j)
end if
end do
end do
end program
But the output file looks like that:
1.000000 1.000000 2.110000
1.000000 2.000000 1.554000
1.000000 3.000000 1.111000
1.000000 2.000000 1.554000
1.000000 2.000000 1.554000
1.000000 0.0000000E+00 3.210000
1.000000 3.000000 1.111000
1.000000 2.000000 1.554000
1.000000 0.0000000E+00 3.210000
1.000000 3.000000 1.111000
1.000000 3.000000 1.111000
1.000000 2.000000 1.554000
1.000000 2.000000 1.554000
I don't understand what is the problem with the if condition, could You so kind to help me a bit?
Thanks a lot !
I wouldn't fix your approach I'd drop it completely. What you've got is an O(n^2) algorithm, adequate for small numbers of lines but on 10^5 lines you're going to execute the if statement 0.5 * 10^10 times. Fortran's fast but that's needlessly wasteful.
I would first sort the file (O(n log n)) then scan it (O(n)) and eliminate duplicates. And I probably wouldn't use Fortran to sort it, I'd use one of the Linux utilities such as sort. Then I'd probably use uniq, and end up doing no Fortran programming at all.
If you want to write the deduplicated file out in the original order then I'd add a line number, then sort, uniqueify, then re-sort.
I believe that recent editions of Windows, ones which support the Powershell, have equivalent commands.
And if I absolutely had to do all this in Fortran I'd write a sort routine (or, rather, pull one out of my bag of tricks) and get on with it. I'd be inclined to read the lines as strings and sort them textually, no messing around with reals and their tricky concept of equality. For 10^5 lines I'd read the entire file into an array, sort it into another array, then carry on.
Finally, I think the logic of your if statement is wonky. It decides whether or not to write a line to the new file based solely on the equality (or not) of the third field, i.e. of amp. It should surely consider all three fields on lines i and j, more like
if ( any( [ x(i)/=x(j), y(i)/=y(j), amp(i)/=amp(j) ] ) ) then
Just to repair the brute force loop, it should be something like this:
do i=1,n
j=1
do while( j.lt.i.and.amp(i) .ne. amp(j))
j=j+1
enddo
if(j.eq.i)write(units,*)x(i),y(i),amp(i)
end do
or
do i=1,n
do j=1,i-1
if ( amp(i) .eq. amp(j) ) exit
enddo
if(j.eq.i)write(units,*)x(i),y(i),amp(i)
end do
Related
I'm learning Fortran and found some strange things when writing with a format (I'm using Fortran onlinegdb)
Program Hello
real, dimension(3,2):: array
array = 0
write(*, '(A,/, A,/, F5.2, F5.2)') &
"1","2",((array(i, j), i = 1,3), j = 1,2)
End Program Hello
I expected
1
2
0.00 0.00
0.00 0.00
0.00 0.00
I get
1
2
0.00 0.00
0.00 0.00
What's wrong?
Vladimir F is correct in saying that the format given does not suit the items that are provided for output: with format reversion after writing two real values, the control goes back to looking at the edit descriptor A but what corresponds to that isn't another character variable. This is not allowed.
However, the format suggested in an earlier revision of that other answer also does not give the output that you expect. If you want to write pairs of numbers on each line relying on an unlimited repeat specification, you'll need to explicitly put the file positioning into the format:
write(*, '(2(A,/),*(2F5.2,:,/))') "1", "2", transpose(array)
Without the / edit in there at the end, the repeat will mean that all elements of the array go in the same record. We also have : there, so that we don't get an extra line break after the final array element.
(I've also transposed the output array, as that's probably what you really mean. The implied do loop in the original output is a little unexpected and makes more sense moving over the final index first.)
With a limited repeat specification, as shown in the corrected form of that answer, format reversion does imply positioning:
write(*, '(2(A,/), 2(F5.2))') "1","2", transpose(array)
After processing the 2(F5.2) reversion has this reused while there are still elements to write out.
In summary, if you are relying on format reversion to "skip" earlier parts of a format while keeping new records, you must correctly mark the part of the whole format to revert to using parentheses. With just the whole format surrounded by parentheses, and no others, format reversion reuses the whole format.
You requested two strings to be printed, each on a new line, then two floating point numbers. That happened correctly.
But then there were still remaining items in the array. The format interpretation started again from the beginning with a new line and again for two strings and two numbers. But the array did not contain any strings...
Try '(A,/, A,/, (F5.2, F5.2))' instead. That will repeat the two-times floating point number format group until there are still numbers to be processed but this time the format does not return to the very beginning. (Note: the old and untested revision of the answer featured an extra repeat count - I did not realize this will disable the format reversion.)
I was trying to write a program to calculate the average without considering the minimum and average of the columns, what is the best way to do this? To begin with, I wanted to simply output the average value of the columns to a file, there are no errors, but nothing is output.
Program Matrix
Implicit None
Real,Allocatable,dimension(:,:)::A
Real,Allocatable,dimension(:):: b
Integer varStr,varStlb
Integer i, j
real summa
Open(1,file='in.txt')
Open(8,file='out.txt')
Do
read(*,*)varStr,varStlb
Allocate(A(1:varStr,1:varStlb),B(1:varStlb))
Read(1,*) ( A(i,:), i = 1,varStr )
do j = 1, varStlb
summa = 0
do i = 1, varStr
summa = summa + a(j,i)
end do
b(j) = summa/varStr
write(8,'(A,F8.2,A)')'b = ',b(j), ' - сумма всех элементов'
end do
Deallocate(A)
Enddo
End Program Matrix
in file is
10.05 -22.0 3.0
4.0 0.0 60.0
8.0 13.0 22.5
As I wrote, there are too many points in your question. Therefore it is hard to make an answer, that covers your question well and does not give you just a solution to your homework without real understanding of the problem.
You have many problems or strange points in your code
I do not see the reason for the outer Do loop. In this loop you will try to read from the file many times. There is no protection to read past the end of the file.
When reading from the file you always allocate your arrays. But you never deallocate them In the next iteration of the outer Do loop you try to allocate them again, but that is not allowed, they are allocated already.
Perhaps you just wanted to read the whole file just once?
I always suggest to learn in steps. First read the file as you need it, print it, verify that it is in the form you needed. Only than compute normal averages. Only then compute averages that exclude maximum and minimum.
Ignoring the maximum and the minimum is simple, just subtract them from the sum and decrease the count of the elements by 2. However, as the rest of your code is so chaotic, it is impossible to just add it there. I will just show a sketch of the procedure:
Normal average of array a(1:n):
avg = sum(a) / n
Average of all elements except the maximum and the minimum:
avg2 = (sum(a) - minval(a) - maxval(a)) / (n - 2)
I am solving a fixed point problem through the following algorithm:
1. specify grids x1,x2,x3,... xn
2. initial guess f=0 on all grids x1, x2, x3, ..., xn
3. update f according to some mapping T. f'=Tf on all grids.
4. calculate distance ||f'-f||. If greater than tolerance, go back to 3; otherwise, end.
5. Write a .txt file to record the solution f.
If let'say, I'm interested in checking out f and f' before the tolerance is reached (i.e. before the program jumps from 4 to 5), is there a way to ask Fortran to write out f and f' once the program is forced to stop? Something like:
IF (stop message received) THEN
PRINT f and f' to files
END IF
I know a variation of doing that is to write f and f' each time the function is updated. But that's perhaps too costly as the algorithm takes 100 seconds for 1 iteration and about 200 iterations to finish, which is approx. 6~7 hours.
Any thoughts and suggestions? Thanks!
So replace
4. calculate distance ||f'-f||. If greater than tolerance, go back to 3; otherwise, end.
5. Write a .txt file to record the solution f.
with
4. calculate distance ||f'-f||. If greater than tolerance, go back to 3.
5. Write a .txt file to record the solution f. end
This seems so obvious I expect I've completely missed the point.
The following data is given in a file:
1 7 5 4 11 0 1 -13 -7
I want to form a 3x3 matrix from these columns. I have tried advance=no but receive a runtime error.
program form_matrix
implicit none
integer:: col,A(9)
open(unit=12,file='q10.txt')
do col=1,9
read(12,*,advance='no')(A(col))
end do
7 format(3i4)
write(*,7)(A(col),col=1,9)
close(12)
end program
Well, a couple of things:
To read into a variable, you need to specify that variable and not a statement containing the variable. A(col) instead of (A(col)).
Then, you are not allowed to use the list directed format (*) with an advance specifier, you need a format (which would be difficult in your case, because of the different widths of the columns).
It will probably be easier to read the whole line into the array in one go, without the loop and using the list directed format:
program form_matrix
implicit none
integer:: col,A(9)
open(unit=12,file='q10.txt')
read(12,*) A
7 format(3i4)
write(*,7)(A(col),col=1,9)
close(12)
end program
* enables list-directed formatting, where individual values are separated by commas or blanks. The type is determined by the RHS of the statement. So you read in a list of nine (because A is nine elements long) integers (because A is integer). For more details on this I suggest the Fortran 2008 Standard, Cl. 10.10 "List-directed formatting"
I am trying to read a list of real numbers from an external file into an array. I do not know how many records fields, separated by spaces, there are per record in a file, and I was therefore planning to use non-advancing I/O to first count the number of records fields, then allocate a real array of sufficient size, and to finally read the file into that same array.
Here is an example input file (where the edit descriptor should be f3.1 for every record field, i.e. a float 3 characters wide with one decimal, and counting the dot; although if I read Metcalf et al. correctly, the decimal is ignored):
1.0 2.0 3.0 4.0 5.0
And a MWE of my program looks like this
program testread
use iso_fortran_env
implicit none
character(len=255) :: filename
filename = 'read.dat'
print *, count_entries(filename)
contains
integer function count_entries(coefficient_file) result(n)
character(len=*), intent(in) :: coefficient_file
!real, dimension(:), allocatable :: coefficients
integer :: fileunit, stat
real :: temp
n=0
open(newunit=fileunit, file=coefficient_file, iostat=stat)
do
read(fileunit,'(f3.1)',advance='no',iostat=stat) temp
if (stat == iostat_end) then
exit
else
n = n + 1
end if
print *, stat, temp
end do
close(fileunit)
! What should happen in the future...
!allocate(coefficients(n))
!read(fileunit,*,iostat=stat) coefficients
end function count_entries
end program testread
If you save the sample input above as read.dat, compile the program with gfortran -o testread{,.f90} and execute it, you will get the following result:
0 1.00000000
0 2.00000000
0 0.300000012
0 0.00000000
0 4.00000000
0 5.00000000
-2 0.00000000
7
In other words, instead of counting 5 entries, it counts 7. And this is not surprising as it, for some reason, sees 7 numbers. But I wonder: why does it see 7 numbers? How can I extend my function to a) be able to also read larger reals and b) read reals of non-uniform width? For example, I would like to be able to read 1.01 1.003 2.1, etc.
It sees six numbers (the last one is an end of record condition) because your format specification specifies that three characters be read each time, but your data is spaced every four columns (three for the data, one for the separating blank).
If your input isn't fixed format (number of columns may vary), then read the whole record (line) into a character(:), allocatable variable and then manually chop that string up.
(Never use a format specification with a specified number of decimal places for input unless you know that your input will always suit that.)
You forgot to account for the spaces. Just use '(f3.1,1x)'.