Fortran; looping over file names with common attributes - fortran

I'm fairly new to Fortran and I am having trouble with my file names, I have a bunch of data in simuln#.res (where 1<#<20), I have multiple different directories with all the same simuln#.res names but they had different input parameters. The code looks like this:
character(len=11) :: theFileA
character(len=12) :: theFileB
character(len=:), allocatable :: fileplace
write(*,*) "the directory with the data sets, use quotations"
read(*,*) fileplace
fileLoop : do j=1,20
if (j .lt. 10) then
write(theFileA, '("simuln", I1,".res")' ) j
open(newunit= iin,file = fileplace//theFileA,status='old')
else
write(theFileB, '("simuln",I2,".res")') j
open(newunit= iin,file = fileplace//theFileB,status='old')
end if
does some stuff with the file
end do fileLoop
The code compiles with a gfortran compiler on my mac, but when I put in my path to the directory with the files, it gives the error simuln1.res does not exist (which it absolutely does, triple checked). I have tried changing the edit descriptor (and making real(j)), but I still get the same thing.
Can anyone help me?

You have fileplace of deferred length ((len=:)), but you appear to not allocate it before attempting the read.
That is, read(*,*) fileplace doesn't, under the F2003 rules of automatic allocation, allocate fileplace to the correct length and assign. That means that later on fileplace could well be being treated as a zero-length character variable ('') in the file to be opened.
To check this hypothesis, try print *, fileplace//theFileA. This could be supported by the fact that the error message refers to just the trailing part of the file's name.
If this is the case, then use a "large" variable. You say 90 characters is as long as you need, so:
character(len=90) :: fileplace ! Adjust length as desired
...
read(*,*) fileplace
...
open (newunit=iin, file=TRIM(fileplace)//theFileA, status='old')
...
Ensure you append the file's name to the trimmed directory name to avoid having spaces between the two parts.
[As a side note, you appear to not need theFileA and theFileB; just use the latter, considering that trailing blanks are ignored. And you may well want to force a trailing '/' on fileplace.]

Related

Reading missing data from a file

I have to read a file that contains numerical data (mostly reals) but there are also some missing data that are denoted by an asterisk(*). I don't know the positions of the asterisks in advance and I have to find the total valid (numerical) data and the total missing data (asterisks).
I tried doing this with a 'select case' nested in a do loop but failed because
I can't use real type for the selector
I don't think I can put the asterisks in a real matrix
The data file looks something like this
1 0.673070
2 0.750597
3 *
4 0.484100
Any suggestions?
Yes in the future, please provide some more information and post a [Minimal, Complete, and Verifiable example] (https://stackoverflow.com/help/mcve) of some code that tries to read it.
But, assuming you know every line has either a real number or an *, I would do something like this:
Character(len=8) :: LineRead
Real :: RealNumber
open(42,file='MyFile.txt')
do (whichever kind of loop you need to control the input)
read(42,'(a8)') LineRead
if (LineRead <> '* ')
read(LineRead,'(f8.6)') RealNumber
! Increment some sort of valid data counter
end if
end do
If you are not familiar with this technique, it is called reading from an internal file. Any character variable can be 'read' this way.

How to automatically adjust string length in the A edit descriptor?

I'm trying to write a string filename in fortran using:
WRITE(FILENAME,'(A27,I3.3,A1,I3.3,A3)') NAME,MYPR,'_',IBL,'.nc'
where NAME is a string of variable lengths, and MYPR and IBL are integers.
I'm searching for a solution where I can dynamically write the format:
'(A27,I3.3,A1,I3.3,A3)',
where A27 will change depending on the length of NAME. I've tried 'A' alone but this caused an error. I'm not sure of what is possible here as many texts do not even cover something similar issues.
Would appreciate some ideas.
The obvious solution would be to use the format string '(A,I3.3,A1,I3.3,A3)', i.e. using just A for the name and letting the compiler choose the correct length. This is working perfectly for me.
As #agentp suggested, you might see issues due to whitespaces in the string. This might be resolved by using trim(name) to get rid of trailing whitespace, or even trim(adjustl(name)) which removes both leading and trailing whitespace. This solution is given below in subroutine print1().
The other option would be to generate the format string dynamically - after all, that is just a string as well. This is quite cumbersome, and an overkill in your situation - see print2().
module test_mod
implicit none
contains
subroutine print1(NAME,MYPR,IBL)
character(len=*),intent(in) :: NAME
integer,intent(in) :: MYPR,IBL
WRITE(*,'(A,I3.3,A1,I3.3,A3)') trim(adjustl(NAME)),MYPR,'_',IBL,'.nc'
end subroutine
subroutine print2(NAME,MYPR,IBL)
character(len=*),intent(in) :: NAME
integer,intent(in) :: MYPR,IBL
character(len=128) :: fmt
write(fmt,*) len_trim(adjustl(NAME))
fmt = '(A'//trim(adjustl(fmt))//',I3.3,A1,I3.3,A3)'
WRITE(*,fmt) trim(adjustl(NAME)),MYPR,'_',IBL,'.nc'
end subroutine
end module
program test
use test_mod
call print1(' Testfile ', 1, 2)
call print2(' Testfile ', 1, 2)
end program
Output:
./a.out
Testfile001_002.nc
Testfile001_002.nc

Write to file using an implicit do loop

I need a help about implicit do loop in Fortran.
This is my simple code:
Program Simple
Implicit none
Integer::i,j
Integer,parameter::N=2,M=3
Real,dimension(N,M)::Pot
Open(1,File='First.txt',Status='old')
Read(1,'(M(f3.1,1x))') ((Pot(i,j),j=1,M),i=1,N)
Close(1)
Open(2,File='Second.txt',Status='Unknown')
Write(2,'(M(i0,1x,i0,1x,f3.1,1x))') ((i,j,Pot(i,j),j=1,M),i=1,N)
Close(2)
Stop
End program Simple
This is the file First.txt:
1.1 1.2 1.3
2.1 2.2 2.3
When I try to execute this program I got a this message:
Unexpected element 'N' in format string
Unexpected element 'M' in format string
I want to keep the name of integer variables N and M in write statement.
Is there any way to also keep their values from declaration part?
You are using M and N in the string (as characters), not as variables. In order to use the variables you need to write their values into the format string:
character(len=128) :: fmtString
!...
write(fmtString,*) M
fmtString = '('//trim(adjustl(fmtString))//'(f3.1,1x))'
Read(1,fmtString) ((Pot(i,j),j=1,M),i=1,N)
And similarly for the write statement.
However, you can probably use list-directed input (Read(1,*)) for the input, and let Fortran figure out the exact format.
Instead of this string manipulation you can use (*(f3.1,1x)) in modern compilers, or if you have an old one just specify a very large number, e.g. (99999(f3.1,1x)). In both cases, the correct number of values will be printed. However, this will result into writing all m*n values in one single line [thanks #agentp for pointing this out].

How to remove leading space when printing with write?

Suppose I have the following code
program fortran
open(900, FILE='SOMETHING')
write(900, *) '21'
end program fortran
The file form will be
21
that is, there is a space before the number. How to get rid of that space?
You can write it as a string:
PROGRAM fortran
OPEN(900,FILE='SOMETHING')
WRITE(900,'(a)') '21'
END PROGRAM FORTRAN
> cat SOMETHING
21
To respond to the comment:
The more explicit way of doing that would be to write the number into a string (you could also use list-directed I/O for this step), remove whitespaces from the string trim and finally output the left-adjusted adjustl:
program test
character(len=23) :: str
write(str,'(ES23.15 E3)') 1.23d0
write(*,'(a)') adjustl(trim(str))
write(str,'(ES14.7 E2)') 0.12e0
write(*,'(a)') adjustl(trim(str))
end program
> ./a.out
1.230000000000000E+000
1.2000000E-01
This solution is probably more complicated then necessary, but it is a very flexible approach that can be extended easily for arbitrary purposes and formats.
In list-directed format (the * in write(unit,*)) the compiler typically inserts a leading space. The first column used to be used to control line printers but that is now deleted from Fortran.
You can use any explicit format you want to get rid of the leading space. For example the general g0 one or the string specific a or the integer-specific i.

Save data in another external file name output.txt?

The program can run, I am not sure how to use open() and save the data in another external file name output.txt. My questions are stated below - please have a look and help.
program start
implicit none
integer ::n
real(kind=8)::x,h,k
real(kind=8),external:: taylorq
x=1.0
n=20
h=exp(x)
k=taylorq(x,n)
open(10,'output.txt') ----------- *question1:(when should i put this open file?)*
write(*,*)"The exact value=",h
write(*,*)"The approximate value=",k
write(*,*)"The error=",h-k
end program start
function taylorq(x,n)
implicit none
integer::n,i
real(kind=8):: x,taylor,taylor2,taylorq,h
h=exp(x)
taylor=1.
taylor2=taylor
write(*,*)"i exact appro error"-----------question2:(actually I want to draw a table with subtitle i, exact, appro, error in each column, is there a nice way to arrange them like eg.we can use %5s)
do i=1,n
taylor=taylor*x/i
taylor2=taylor2+taylor
write(10,*)i,h,taylor2,taylor2-h --------question3:*(I want to save the data written here into file output.txt)*
end do
close(10)
taylorq=taylor2
end function taylorq
1. where to open
You should put open(10,...) so it executes before any write(10,...) -- or read(10,...) if this was input.
Since your writes occur in the function taylorq, you should open() before the statement that calls taylorq.
For programs that do very large computations, which Fortran is suited/famous for, it is often best to do
all file open's very near the beginning of the program, so that if there is a problem opening any file,
it is caught and fixed without wasting hours or days of work. But your program is much simpler than that.
2. formatting
Yes, Fortran can do formatted output -- and also formatted input. Instead of a text string with
interpolated specifiers (like C and the C part of C++, and Java, and awk and perl and shell) it uses specifiers
with optionally interpolated text values, and the specifiers are written with the format letter on
the left followed by the width (almost always) and other parameters (sometimes).
You can either put the format directly in the WRITE (or READ) statement, or in a separate FORMAT
statement referred to by its label in the I/O statement.
write (10, '(I4,F10.2,F10.2,F10.2)' ) i,h,taylor2,taylor2-h
or
write (10, 900) i,h,taylor2,taylor2-h
! this next line can be anywhere in the same program-unit
900 format (I4,F10.2,F10.2,F10.2)
Unlike C-family languages, Fortran will always output the specified width; if the value doesn't fit,
it prints asterisks ***** instead of forcing the field wider (and thus misaligned) (or truncating as
COBOL does!). Your series grows fast enough you might want to use scientific notation like E10.3.
(The format letters can be in either case, but I find them easier to read in upper. YMMV.)
There are many, MANY, more options. Any textbook or your compiler manual should cover this.