How to write to file elements of arrays in a particular pattern - fortran

I want to write to file elements of the three arrays: k= (/1, 2 /), kp = (/1, 2 /), w(k,kp) = (/1,2 ,3,4/)
in the following pattern, using Fortran:
k kp w(k,kp)
1 1 1
1 2 2
2 1 3
2 2 4
I know how to write for column "kp" and "w", but how can I write column "k" ?
I have my code as:
write(20,*) "k" , "kp", "W"
do i = 1,2
do j = 1, 2
write (20,*) k( ) kp(j) , W(i,j)
end do
end do

Is this homework problem?
program foo
implicit none
integer :: i, j, k(2) = [1,2], kp(2) = [1,2]
integer :: w(2,2) = reshape([1,3,2,4], [2,2])
do j = 1, 2
do i = 1, 2
write(*,'(*(1X,I0))') k(j), kp(i), w(k(j),kp(i))
end do
end do
end program foo

Related

Find the sum of each rows and each columns

need to use SUM() and dim
the problem in the sum() algorithm does not calculate correctly, I can’t fix it, I need someone’s help
program main
use environment
implicit none
character(*), parameter :: input_file = "../data/input.txt", output_file = "output.txt"
integer :: In = 0, Out = 0, rows = 0, columns = 0!, i = 0
integer, allocatable :: A(:,:)
integer :: res_rows = 0, res_columns = 0
open (file=input_file, newunit=In)
read(In, *) rows, columns
allocate(A(rows, columns))
read (In, *) A
close (In)
res_rows = sum(A(1:columns+1,1), dim=1)
res_columns = sum(A(1:rows+1,1), dim=1)
!outout data
open (file=output_file, encoding=E_, newunit=Out, position='append')
write(*,*)"rows:",res_rows
write(*,*)"columns:",res_columns
close (Out)
end program main
input data from txt file
4 3
1 1 2
4 3 4
1 1 2
4 3 2
output data to txt file
rows: 4 11 4 9
columns: 10 8 10
Fortran is a column-major language. Your read(in,*) a is populating the matrix in the wrong order. Try writing out the first row of your matrix a. Your use of the sum intrinsic is also wrong. See below.
program main
implicit none
character(*), parameter :: input_file = "a.dat"
integer i, in, out, rows, columns
integer, allocatable :: a(:,:)
integer :: res_rows = 0, res_columns = 0
open(file=input_file, newunit=in, status='old')
read(in, *) rows, columns
allocate(a(rows, columns))
do i = 1, rows
read(in,*) a(i,:)
end do
close(in)
print '(A,4(1X,I0))', 'Sum of each row:', sum(a,dim=2)
do i = 1, rows
print '(3I3,A,I0)', a(i,:),' = ', sum(a(i,:))
end do
print *
print '(A,4(1X,I0))', 'Sum of each column:', sum(a,dim=1)
do i = 1, columns
print '(4I3,A,I0)', a(:,i),' = ',sum(a(:,i))
end do
end program main

Why it's printing different for this two loops

So i have for example t=[1.0, 1.0, 1.6, 1.125, 1.5]
I want to print the indices for the elements that have the minimum value min(t) but i want them to start from 1
So for this example i want to print 1 2
It's working when i do this:
for j in range(len(t)):
if t[j]==min(t):
print j+1,`
Output :
1 2
But it's not working with this:
for j in t:
if j==min(t):
a=t.index(j)
print a+1,`
Output :
1 1
Why is that?
Your error is here
a = t.index(j)
t.index(j) is going to give you the first index of that value.
You can make this:
m = min(t)
cont = 0
for j in t:
if j == m:
print cont+1,
cont += 1
Or
for idx, val in enumerate(t):
if val == m:
print idx + 1,

Writing a blank instead of an integer in Fortran

I have a few 110-element vectors. They sometimes have a value from 0 to 9, but their default value is -1. I'd like to print a blank if a cell's value is -1; print their value otherwise.
I'm printing several things in an output line so I can't use an if with two writes. Passing the values to a character vector worked but I can't help but think there must be a better way.
My attempt:
program integer_print_blank_test
implicit none
integer, dimension(9) :: longint
character(len=3), dimension(9) :: longchar
integer :: i, j
do i = 0, 2
write(*,*) (longint(3*i+j), j = 1, 3)
end do
longint = -1
longint(1) = 1
longint(4) = 3
longint(9) = 7
write(*,*) "longint"
do i = 0, 2
write(*,*) (longint(3*i+j), j = 1, 3)
end do
do i = 1, 9
write(longchar(i),"(I3)") longint(i)
end do
write(*,*) "longchar"
do i = 0, 2
write(*,*) (longchar(3*i+j), j = 1, 3)
end do
write(*,*) "only positives in longchar"
longchar = " "
do i = 1, 9
if (longint(i) > -1) then
write(longchar(i),"(I3)") longint(i)
end if
end do
do i = 0, 2
write(*,*) (longchar(3*i+j), j = 1, 3)
end do
end program integer_print_blank_test
You might think this is a better way. Define a function such as
ELEMENTAL FUNCTION borf(int) RESULT(str)
INTEGER, INTENT(in) :: int
CHARACTER(len=2) :: str
str = ' '
IF (int>-1) WRITE(str,'(i2)') int
END FUNCTION borf
and use it like this
WRITE(*,*) borf(longint)

data entrance error Fortran

I'm learning how to programming with fortran90 and i need receive data from a txt file by the command prompt (something like that:
program.exe"<"data.txt).
at the Input txt file I'll always have a single line with at least 6 numbers till infinity.
if the data was wrote line by line it runs fine but as single line I'm receiving the error: "traceback:not available,compile with - ftrace=frame or - ftrace=full fortran runtime error:end file"
*note: i'm using Force fortran 2.0
here is example of data:
0 1 0.001 5 3 1 0 -9 3
edit: just clarifying: the code is working fine itself except for the read statement, which is a simple "read*,". I want know how To read a entire line from a txt once the entrance will be made by the promt command with stream direction.
( you can see more about that here: https://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/redirection.mspx?mfr=true).
there is no need to read the code, i've posted it just for knowledge.
I'm sorry about the whole inconvenience.
here is the code so far:
program bissecao
implicit none
integer::cont,int,e,k,intc,t1,t2,t3
doubleprecision::ii,is,pre,prec,erro,somaa,somab,xn
doubleprecision,dimension(:),allocatable::co
t1=0
t2=0
t3=0
! print*,"insira um limite inf da funcao"
read*,ii
!print*,"insira o limite superior da func"
read*,is
! print*,"insira a precisÆo admissivel"
read*,pre
if (erro<=0) then !elimina criterio de parada negativo ou zero
Print*,"erro"
go to 100
end if
!print*,"insira a qtd iteracoes admissiveis"
read*,int
!print*,"insira o grau da f(x)"
read*,e
if (e<=0) then ! elimina expoente negativo
e=(e**2)**(0.5)
end if
allocate(co(e+1))
!print*, "insira os coeficientes na ordem:&
! &c1x^n+...+(cn-1)x^1+cnx^0"
read(*,*)(co(k),k=e+1,1,-1)
somab=2*pre
intc=0
do while (intc<int.and.(somab**2)**0.5>pre.and.((is-ii)**2)**0.5>pre)
somab=0
somaa=0
xn =(ii+is)/2
do k=1,e+1,1
if (ii /=0) then
somaa=ii**(k-1)*co(k)+somaa
else
somaa=co(1)
end if
! print*,"somaa",k,"=",somaa
end do
do k=1,(e+1),1
if (xn/=0) then
somab=xn**(k-1)*co(k)+somab
else
somab=co(1)
end if
!print*,"somab",k,"=",somab
end do
if ((somaa*somab)<0) then
is=xn
else if((somaa*somab)>0)then
ii=xn
else if ((somaa*somab)==0) then
xn=(ii+is)/2
go to 100
end if
intc =intc+1
prec=is-ii
if ((((is-ii)**2)**.5)< pre) then
t3=1
end if
if (((somab**2)**.5)< pre) then
t2=1.
end if
if (intc>=int) then
t1=1
end if
end do
somab=0
xn=(ii+is)/2
do k=1,(e+1),1
if (xn/=0) then
somab=xn**(k-1)*co(k)+somab
else
somab=co(1)
end if
end do
100 write(*,'(A,F20.15,A,F20.15,A,A,F20.15,A,F20.15,A,I2)'),"I:[",ii,",",is,"]","raiz:",xn,"Fraiz:",somab,"Iteracoes:",intc
end program !----------------------------------------------------------------------------
In your program, you are using the "list-directed input" (i.e., read *, or read(*,*))
read *, ii
read *, is
read *, pre
read *, int
read *, e
read *, ( co( k ), k = e+1, 1, -1 )
which means that the program goes to the next line of the data file after each read statement (by neglecting any remaining data in the same line). So, the program works if the data file (say "multi.dat") consists of separate lines (as suggested by OP):
0
1
0.001
5
3
1 0 -9 3
But now you are trying to read an input file containing only a single line (say "single.dat")
0 1 0.001 5 3 1 0 -9 3
In this case, we need to read all the values with a single read statement (if list-directed input is to be used).
A subtle point here is that the range of array co depends on e, which also needs to be read by the same read statement. A workaround might be to just pre-allocate co with a sufficiently large number of elements (say 100) and read the data in a single line, e.g.,
integer :: k
allocate( co( 100 ) )
read *, ii, is, pre, int, e, ( co( k ), k = e+1, 1, -1 )
For completeness, here is a test program where you can choose method = 1 or 2 to read "multi.dat" or "single.dat".
program main
implicit none
integer :: int, e, k, method
double precision :: ii, is, pre
double precision, allocatable :: co(:)
allocate( co( 1000 ) )
method = 1 !! 1:multi-line-data, 2:single-line-data
if ( method == 1 ) then
call system( "cat multi.dat" )
read*, ii
read*, is
read*, pre
read*, int
read*, e
read*, ( co( k ), k = e+1, 1, -1 )
else
call system( "cat single.dat" )
read*, ii, is, pre, int, e, ( co( k ), k = e+1, 1, -1 )
endif
print *, "Input data obtained:"
print *, "ii = ", ii
print *, "is = ", is
print *, "pre = ", pre
print *, "int = ", int
print *, "e = ", e
do k = 1, e+1
print *, "co(", k, ") = ", co( k )
enddo
end program
You can pass the input file from standard input as
./a.out < multi.dat (for method=1)
./a.out < single.dat (for method=2)
Please note that "multi.dat" can also be read directly by using "<".

Populate a constant array in order specified by other constants?

Is there a way to populate a constant array in an order specified by other constant variables?
So, in effect this:
integer, parameter :: ired = 1
integer, parameter :: iblue = 2
real, parameter :: myarr(2,3)
myarr(ired, :) = [1,0,0]
myarr(iblue,:) = [0,0,1]
Except the above of course will not compile. Is there a way to get to this in some way?
To generalize #HPM's answer to the case where ired and iblue etc may be discontiguous (e.g, 1 and 3), combined use of implied do-loop + array constructor might be useful. Because arrays in Fortran are column-major, I have aligned the vectors in a matrix such that [ vec1, vec2, ..., vecN ] where vecX is a 3-vector.
integer :: k
integer, parameter :: ired = 1, iblue = 3, mxvec = 4, ndim = 3, zero(3) = [0,0,0]
integer, dimension( ndim * mxvec ), parameter :: &
red = [ (zero, k=1,ired-1 ), [1,1,1], (zero, k=ired+1, mxvec) ], &
blue = [ (zero, k=1,iblue-1), [7,7,7], (zero, k=iblue+1,mxvec) ]
integer, parameter :: myarr( ndim, mxvec ) = reshape( red + blue, [ ndim, mxvec ] )
print "(a,/100(3i2/))", "red = ", red
print "(a,/100(3i2/))", "blue = ", blue
print "(a,/100(3i2/))", "myarr = ", myarr
print *, "myarr( :, ired ) = ", myarr( :, ired )
print *, "myarr( :, iblue ) = ", myarr( :, iblue )
Result:
red =
1 1 1
0 0 0
0 0 0
0 0 0
blue =
0 0 0
0 0 0
7 7 7
0 0 0
myarr =
1 1 1
0 0 0
7 7 7
0 0 0
myarr( :, ired ) = 1 1 1
myarr( :, iblue ) = 7 7 7
No, there is no way to assign values to a parameter after program start-up; that's exactly what the attribute parameter is intended to prevent.
You could write
real, parameter :: myarr(2,3) = reshape([1.0,0,0,0,0,1],[2,3])
to initialise myarr. Note that the elements are provided to reshape in the array element order specified by Fortran (ie column major); here it happens to be the same as if you had specified them in row major order. And note that in Fortran initialization means, precisely, setting a value in the declaration statement, which is how parameters acquire values.
I don't immediately see any way to use ired and iblue in the intialisation but I'm struggling to see that as a problem.
EDIT, after OP's comment:
I guess you could write something like
INTEGER, PARAMETER :: ired = 1
INTEGER, PARAMETER :: iblue = 2
REAL, PARAMETER, DIMENSION(2,3) :: rows = reshape([1,0,0,0,0,1],[2,3])
REAL, PARAMETER :: myarr(2,3) = RESHAPE([rows(ired,:), rows(iblue,:)], [2,3])
and now you only have to swap the values of ired and blue to change myarr. And the only thing you might forget is why you wrote such convoluted code !