Related
I'm trying to write a time variable from a hydrodynamic model into a netcdf file (unlimited dimension variable). I've attached a simplified code example in Fortran90 that highlights my issue.
The subroutine to write the netcdf file is called multiple times during a simulation depending on a user specified output interval (10 times for this example). I can create the file and add attributes for the first time the subroutine is called.
I can't get the start and count variables correct to write the time variable to the file during the subsequent calls of the subroutine. This is the error, at the writing the model time variable, I receive when trying to compile the code: Error: There is no specific function for the generic 'nf90_put_var'
PROGRAM test_netcdf
IMPLICIT NONE
INTEGER :: N
REAL :: time_step = 2.
! Call efdc_netcdf 10 times
DO N=1,10
CALL efdc_netcdf(N, time_step)
time_step=time_step + 1.
ENDDO
END PROGRAM test_netcdf
************************************
! Create NetCDF file and write variables
SUBROUTINE efdc_netcdf(N, time_step)
USE netcdf
IMPLICIT NONE
LOGICAL,SAVE::FIRST_NETCDF=.FALSE.
CHARACTER (len = *), PARAMETER :: FILE_NAME = "efdc_test.nc"
INTEGER :: ncid, status
INTEGER :: time_dimid
INTEGER :: ts_varid, time_varid
INTEGER :: start(1), count(1)
INTEGER :: deltat
INTEGER :: N
REAL :: time_step
start=(/N/)
count=(/1/)
! Create file and add attributes during first call of efdc_netcdf
IF(.NOT.FIRST_NETCDF)THEN
status=nf90_create(FILE_NAME, NF90_CLOBBER, ncid)
! Define global attributes once
status=nf90_put_att(ncid, NF90_GLOBAL, 'format', 'netCDF-3 64bit offset file')
status=nf90_put_att(ncid, NF90_GLOBAL, 'os', 'Linux')
status=nf90_put_att(ncid, NF90_GLOBAL, 'arch', 'x86_64')
! Define deltat variable
status=nf90_def_var(ncid,'deltat',nf90_int,ts_varid)
! Define model time dimension
status=nf90_def_dim(ncid,'efdc_time',nf90_unlimited,time_dimid)
! Define model time variable
status=nf90_def_var(ncid,'efdc_time',nf90_real,time_dimid,time_varid)
status=nf90_enddef(ncid)
! Put deltat during first call
deltat=7
status=nf90_put_var(ncid, ts_varid, deltat)
FIRST_NETCDF=.TRUE.
ENDIF
! Put model time variable
status=nf90_put_var(ncid, time_varid, time_step, start=start, count=count)
! Close file at end of DO loop
IF(N.EQ.10) THEN
status=nf90_close(ncid)
ENDIF
RETURN
END SUBROUTINE efdc_netcdf
The issue is in the line the compiler flags:
status=nf90_put_var(ncid, time_varid, time_step, start=start, count=count)
You are (correctly) trying to write a scalar variable, time_step, into a specific index (start) along variable time_varid, which is defined on a 1-d, infinite-extent dimension. However, in this case, the optional argument count isn't meaningful; you are writing the scalar, and count can only ever be 1. As a result, the fortran bindings for a nf90_put_var() taking a single scalar for input don't have the optional argument defined for count, and that's why you're getting the "no specific function for the generic' nf90_put_var" error from the compiler. This is all perfectly reasonable, but neither the error message nor the docs are super helpful in figuring out how to solve the problem.
You can fix your code by putting the time_step data into a real, dimension(1) variable, and putting that, instead; but easiest is to just get rid of the count specification, which isn't necessary here anyway:
status=nf90_put_var(ncid, time_varid, time_step, start=start)
I have an existing file written by a Fortran program (formatted) and I would like to add few lines at the beginning of the file. The idea is to do so without making a copy of the original file.
I could add a line at the end of the file with:
open(21,file=myfile.dat,status='old',action='write',
form='formatted',position="append")
write(21,*) "a new line"
but when I tried:
open(21,file=myfile.dat,status='old',action='write',
form='formatted',position="rewind")
write(21,*) "a new line"
it overwrites the whole file.
It might be impossible.
At least, I would be glad to have a confirmation that it is effectively impossible.
Yes, it is impossible. With position= you only set the position for writing. Normally you just delete everything past the current record by a write in a sequential file. You may be able to adjust a record at the beginning in a direct access file, but also not just add something at the beginning. You have to make a copy first.
If you are using un-formatted data and know how many lines to expect, try using the direct access file read/write method. This has the potential to store information for each line in a 'record' which can be accessed later much like an array.
In order to append to the beginning, simply create as many empty records as you will have lines in your 'header' at the beginning of the file then go back and change their values to the actual lines you want them to be later.
Example of Direct Access file io:
CHARACTER (20) NAME
INTEGER I
INQUIRE (IOLENGTH = LEN) NAME
OPEN( 1, FILE = 'LIST', STATUS = 'REPLACE', ACCESS = 'DIRECT', &
RECL = LEN )
DO I = 1, 6
READ*, NAME
WRITE (1, REC = I) NAME ! write to the file
END DO
DO I = 1, 6
READ( 1, REC = I ) NAME ! read them back
PRINT*, NAME
END DO
WRITE (1, REC = 3) 'JOKER' ! change the third record
DO I = 1, 6
READ( 1, REC = I ) NAME ! read them back again
PRINT*, NAME
END DO
CLOSE (1)
END
code source, see section on "Direct Access Files":
http://oregonstate.edu/instruct/ch590/lessons/lesson7.html
It is possible !!! Here is a sample program which could accomplish the task.
! Program to write after the end line of a an existing data file
! Written in fortran 90
! Packed with an example
program write_end
implicit none
integer :: length=0,i
! Uncomment the below loop to check example
! A file.dat is created for EXAMPLE defined to have some 10 number of lines
! 'file.dat may be the file under your concern'.
! open (unit = 100, file = 'file.dat')
! do i = 1,10
! write(100,'(i3,a)')i,'th line'
! end do
! close(100)
! The below loop calculates the number of lines in the file 'file.dat'.
open(unit = 110, file = 'file.dat' )
do
read(110,*,end=10)
length= length + 1
end do
10 close(110)
! The number of lines are stored in length and printed.
write(6,'(a,i3)')'number of lines= ', length
! Loop to reach the end of the file.
open (unit= 120,file = 'file.dat')
do i = 1,length
read(120,*)
end do
! Data is being written at the end of the file...
write(120,*)'Written in the last line,:)'
close(120)
end
I have opened a file to write a number. I have to write the number at the end of the file so
how to go to the last line to write on it?
You should open the file with
open(..., position="append",...)
Alternatively, you can inquire for the size of the file
inquire(...,size=some_integer_variable,...)
then if the file is a direct access file, you can use this size to calculate the record number of the final record. Alternatively, if the access mode is "stream", you can use
write(..., pos=some_integer_variable)
to write starting at the end of the file.
I've been using the same trick for years, and would be interested in a more elegant way but I can propose you the following method. Note that it is less and less efficient as the file increases in number of lines. Note also that this part of code could endup in an elegant module dedicated to playing with input/output.
Open your file
open(11, file='monfichier')
Compute how many lines there are in your file
nbline = 0 ! should have been declared as an integer
do while(.true.)
read(11,*,iostat=ios) ! ios should have been declared as an integer
if( ios > 0 ) then
stop 'problem somewhere'
else if( ios < 0 ) then ! end of file is reached
exit
else
nbline = nbline + 1
end if
end do
close(11)
at this step, you have the total number of lines stored in variable nbline.
If you want to print something at the Nth line before the last line, then
open(11, file='monfichier')
do i = 1, nbline - N ! see my nota bene at the end of my answer; i and N are integers
read(11,*)
end do
write(11,*)'hello world'
Et voilĂ !
N.B. : Please be carefull in the way you count for nbline-N or nbline-(N-1), depending on exactly what you want.
subroutine to_last_rec (luout)
! purpose: position to last record of file
implicit none
integer :: luout
logical :: ende
! first executable statement
ende = .FALSE.
do while ( .NOT. ende)
read (luout,*,end=100)
enddo
100 return
end subroutine to_last_rec
PROGRAM example
IMPLICIT NONE
INTEGER :: ierr
OPEN(UNIT=13,FILE="ex.dat")
CALL FSEEK(13, 0, 2, ierr)
! DO WHATEVER YOU WANT THEN
CLOSE(13)
END PROGRAM example
the call to fseek goes to the end of the file ( used like that, check the usage http://docs.oracle.com/cd/E19957-01/805-4942/6j4m3r8ti/index.html)
i'm a grad student trying to work with atmospheric data files provided by NOAA. I have a code that reads the data but only writes the first four columns of data. I know that the subroutine at the end of the code unpacks the data. I was thinking maybe if I take out the conditional statements then it might write the entire data. But that doesn't work. I need to develop this code so that it writes the entire data file and not just the first four elements. Any help is appreciated. The code is shown below:
PROGRAM CHK_DATA
!-------------------------------------------------------------------------------
! Simple program to dump the first few elements of the data array for each
! record of an ARL packed meteorological file. Used for diagnostic testing.
! Created: 23 Nov 1999 (RRD)
! 14 Dec 2000 (RRD) - fortran90 upgrade
! 18 Oct 2001 (RRD) - expanded grid domain
! 03 Jun 2008 (RRD) - embedded blanks
!-------------------------------------------------------------------------------
REAL, ALLOCATABLE :: RDATA(:,:)
CHARACTER(1), ALLOCATABLE :: CPACK(:)
CHARACTER(4) :: KVAR, MODEL
CHARACTER(50) :: LABEL
CHARACTER(80) :: FDIR, FILE
CHARACTER(3072) :: HEADER
LOGICAL :: FTEST
!-------------------------------------------------------------------------------
INTERFACE
SUBROUTINE UNPACK(CPACK,RDATA,NX,NY,NEXP,VAR1)
CHARACTER(1),INTENT(IN) :: CPACK(:)
REAL, INTENT(OUT) :: RDATA(:,:)
INTEGER, INTENT(IN) :: NX,NY,NEXP
REAL, INTENT(IN) :: VAR1
END SUBROUTINE
END INTERFACE
!-------------------------------------------------------------------------------
! directory and file name
WRITE(*,*)'Enter directory name:'
READ(*,'(a)')FDIR
FDIR=ADJUSTL(FDIR)
WRITE(*,*)'Enter file name:'
READ(*,'(a)')FILE
FILE=ADJUSTL(FILE)
! test for meteo file existence
KLEN=LEN_TRIM(FDIR)
INQUIRE(FILE=FDIR(1:KLEN)//FILE,EXIST=FTEST)
IF(.NOT.FTEST)THEN
WRITE(*,*)'Unable to find file: ',FILE
WRITE(*,*)'On local directory : ',FDIR(1:KLEN)
STOP
END IF
! open file to decode the standard label (50) plus the
! fixed portion (108) of the extended header
OPEN(10,FILE=FDIR(1:KLEN)//FILE,RECL=158,ACCESS='DIRECT',FORM='UNFORMATTED')
! decode the standard portion of the index record
READ(10,REC=1)LABEL,HEADER(1:108)
READ(LABEL,'(5I2,4X,A4)')IYR,IMO,IDA,IHR,IFC,KVAR
WRITE(*,'(A,4I5)')'Opened file : ',IYR,IMO,IDA,IHR
IF(KVAR.NE.'INDX')THEN
WRITE(*,*)'WARNING Old format meteo data grid'
WRITE(*,*)LABEL
WRITE(*,*)HEADER(1:108)
STOP
END IF
! decode extended portion of the header
READ(HEADER(1:108),'(A4,I3,I2,12F7.0,3I3,I2,I4)',ERR=900) &
MODEL, ICX, MN, &
POLE_LAT, POLE_LON, REF_LAT, &
REF_LON, SIZE, ORIENT, &
TANG_LAT, SYNC_XP, SYNC_YP, &
SYNC_LAT, SYNC_LON, DUMMY, &
NX, NY, NZ, &
K_FLAG, LENH
! close file and reopen with proper length
CLOSE (10)
NXY = NX*NY
LEN = NXY+50
OPEN(10,FILE=FDIR(1:KLEN)//FILE,RECL=LEN,ACCESS='DIRECT',FORM='UNFORMATTED')
! print file diagnostic
WRITE(*,'(A,4I5)')'Grid size and lrec: ',NX,NY,NXY,LEN
WRITE(*,'(A,I5)') 'Header record size: ',LENH
! allocate array space
ALLOCATE (RDATA(NX,NY), STAT=KRET)
ALLOCATE (CPACK(NXY), STAT=KRET)
! read entire file and print headers
KREC=1
100 READ(10,REC=KREC,ERR=800)LABEL,(CPACK(K),K=1,NXY)
READ(LABEL,'(6I2,2X,A4,I4,2E14.7)',ERR=900) IY,IM,ID,IH,IF,KL, &
KVAR,NEXP,PREC,VAR1
WRITE(*,'(A)')LABEL
IF(KVAR.NE.'INDX') CALL UNPACK(CPACK,RDATA,NX,NY,NEXP,VAR1)
READ(*,*,END=800)
KREC=KREC+1
GO TO 100
800 STOP
900 WRITE(*,*)'ERROR: decoding header'
WRITE(*,*)LABEL
WRITE(*,*)HEADER(1:108)
END PROGRAM chk_data
!-------------------------------------------------------------------------------
SUBROUTINE UNPACK(CPACK,RDATA,NX,NY,NEXP,VAR1)
CHARACTER(1),INTENT(IN) :: CPACK(:)
REAL, INTENT(OUT) :: RDATA(:,:)
INTEGER, INTENT(IN) :: NX,NY,NEXP
REAL, INTENT(IN) :: VAR1
! only required when dealing with F95 compilers
! replace ICHAR below with internally defined JCHAR function
! CHARACTER MYCHR*1
! JCHAR(MYCHR)=IAND(ICHAR(MYCHR),255)
SCALE=2.0**(7-NEXP)
VOLD=VAR1
INDX=0
DO J=1,NY
DO I=1,NX
INDX=INDX+1
RDATA(I,J)=(ICHAR(CPACK(INDX))-127.)/SCALE+VOLD
VOLD=RDATA(I,J)
IF(I.LE.2.AND.J.LE.2) &
WRITE(*,'(3I5,E12.4)')J,I,ICHAR(CPACK(INDX)),RDATA(I,J)
IF(I.GE.(NX-1).AND.J.GE.(NY-1)) &
WRITE(*,'(3I5,E12.4)')J,I,ICHAR(CPACK(INDX)),RDATA(I,J)
END DO
VOLD=RDATA(1,J)
END DO
END SUBROUTINE unpack
Did you notice the comment
! only required when dealing with F95 compilers
! replace ICHAR below with internally defined JCHAR function
! CHARACTER MYCHR*1
! JCHAR(MYCHR)=IAND(ICHAR(MYCHR),255)
Try to uncomment the definition, and change the calls to CHAR to calls to JCHAR.
ICHAR is an intrinsic function in Fortran that behaves different, than the code expects. It all seems suspicious, because the JCHAR does nothing to numbers below 256.
For further porting consider using IMPLICIT NONE and modules.
---EDIT ---
Now I see your conditionals. If you remove
IF(I.LE.2.AND.J.LE.2) &
and
IF(I.GE.(NX-1).AND.J.GE.(NY-1)) &
what exactly does the code do? We do not have the data.
(I know, there are a lot of the data files on the page you downloaded your program, but I am not going to the all the work for you, sorry.)
Cant you find a complete code to work with the data anywhere?
I am using FORTRAN to read in data from an ASCII text file. The file contains multiple data values per line but the number of values per line is not constant.
101.5 201.6 21.4 2145.5
45.6 21.2
478.5
...
Normally after a read statement, Fortran would go to the next line. What I want to be able to do is read one data value at a time. If it hits the end of the line, it should just continue reading on the next line. Is this possible?
As pointed out by IRO-bot in their comment to your question, the answer has already been given by M.S.B. Below I have merely provided some code illustrating that answer (as M.S.B.'s post contained none):
program test
character(len=40) :: line
integer :: success, i, indx, prev, beginning
real :: value
open(1,file='test.txt')
do
read(1,'(A)',iostat=success) line
if (success.ne.0) exit
prev = 1
beginning = 1
do i=1,len(line)
! is the current character one out of the desired set? (if you
! have got negative numbers as well, don't forget to add a '-')
indx = index('0123456789.', line(i:i))
! store value when you have reached a blank (or any other
! non-real number character)
if (indx.eq.0 .and. prev.gt.0) then
read(line(beginning:i-1), *) value
print *, value
else if (indx.gt.0 .and. prev.eq.0) then
beginning = i
end if
prev = indx
end do
end do
close(1)
end program test
When running this program using the sample lines you provided the output is
101.5000
201.6000
21.40000
2145.500
45.60000
21.20000
478.5000
I hope you will find this helpful.