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)
Related
I create a fortran code to calculate the temperature of a cfd model. This code will be called on every iteration of a steady state simulation and calculate the temperature. On every calling of my code/iteration i want my fortran code also save the temperature field on a txt file and save. After calculating the temperature field and saving the values in TFIELD(100;1:6) the part with saving in txt file looks like:
OPEN(UNIT=35,FILE='W:\temperaturField.txt',
&FORM ='FORMATTED',STATUS='UNKNOWN',
&ACTION='READWRITE')
WRITE (35,FMT='5(F8.3,2X))') TFIELD(100,1:6)
With this code it only overwrites the first line of my txt file on every iteration. But i want to paste every TFIELD(100,1:6) array on a new line. How can i do this?
Add POSTITION='APPEND' to the OPEN:
OPEN(UNIT=35,FILE='W:\temperaturField.txt',
&FORM ='FORMATTED',STATUS='UNKNOWN',POSITION='APPEND',
&ACTION='READWRITE')
It seems like you are opening and closing the file for each iteration. This is a quick and dirty method if you need to debug, but it's slow.
If you want to do that, you might want to do what #Jack said: Include a POSITION='APPEND' to the OPEN statement to set the position to write the data to the end of the file. Also, you need to make sure that you close it every time.
A better (more efficient) method would be to keep the file open for the whole time. I'd do that with a module:
module temp_writer_module
implicit none
integer :: temp_writer_unit
logical :: is_opened = .FALSE.
private :: temp_writer_unit, is_opened
contains
subroutine temp_writer_open()
integer :: ios
character(len=100) :: iomsg
if (is_opened) then
print*, "Warning: Temperature file already openend"
return
end if
open(newunit=temp_writer_unit, file='W:\temperatureField', &
form='FORMATTED', status='UNKNOWN', action='WRITE', &
iostat=ios, iomsg=iomsg)
if (ios /= 0) then
print*, "Error opening temperature file:"
print*, trim(iomsg)
STOP
end if
is_opened = .TRUE.
end subroutine temp_writer_open
subroutine temp_writer_close()
if (.not. is_opened) return
close(temp_writer_unit)
is_opened = .FALSE.
end subroutine temp_writer_close
subroutine temp_writer(temps)
real, intent(in) :: temps(6)
integer :: ios
character(len=100) :: iomsg
if (.not. is_opened) call temp_writer_open()
write(temp_writer_unit, *, iostat=ios, iomsg=iomsg) temps
if (ios /= 0) then
print*, "Error writing to temperature file:"
print*, trim(iomsg)
end if
end subroutine temp_writer
end module temp_writer_module
Then you can use it in your program like this:
subroutine calc_temps(...)
use temp_writer_module
<variable declarations>
<calculations>
call temp_writer(tfield(100, 1:6))
end subroutine calc_temps
Just don't forget to call the temp_writer_close routine before your program ends.
I use fortran to create netcdf files. I have this problem: I have no choice than to use a loop to define some of my variables (and assign the attribute values). Then, when I want to provide the values of the variables (i.e, nf90_put_var), it only recalls the last variable that has been defined... I have tried many things to resolve the problem but I didn't succeed. Someone could help me ?
Here is a small part of my script:
DO IP=1,N(PTS)
Param_name='var1'
params(I,IPTS)=INT(I,IPTS,IP)
! Define Netcdf Variable
IERREU = nf90_def_var(ncid, Param_name, nf90_real, dimid, ParVarID)
IF (IERREU.NE.0) THEN
CALL check_err (IERREU)
STOP
ENDIF
ENDDO
! End define mode
IERREU = nf90_enddef(ncid)
IF (IERREU.NE.0) THEN
CALL check_err (IERREU)
STOP
ENDIF
! Write the data in netcdf
IERREU = nf90_put_var(ncid,parvarID, params)
IF (IERREU.NE.0) THEN
CALL check_err (IERREU)
STOP
ENDIF
You must store the parVarId for each variable separately. Perhaps store it in an array. You now overwrite it with each call to nf90_def_var.
integer ParVarIds(N(PTS))
DO IP=1,N(PTS)
...
IERREU = nf90_def_var(ncid, Param_name, nf90_real, dimid, ParVarIds(IP))
...
ENDDO
DO IP=1,N(PTS)
...
IERREU = nf90_put_var(ncid,parVarIds(IP), something)
...
ENDDO
I am working with a FEM program written in Fortran language. It is a mechanical analysis that involves multiple set of material. The important variables are saved into modules that have the following form
MODULE element
implicit none
save
TYPE proel_type
INTEGER:: prop1
INTEGER:: prop2
REAL:: prop3
REAL:: prop4
...
INTEGER, ALLOCATABLE :: list1(:)
REAL, ALLOCATABLE :: array1(:)
...
INTEGER:: LTYPE
...
END TYPE proel_type
TYPE(proel_type), ALLOCATABLE:: proel(:)
INTEGER:: prop1
INTEGER:: prop2
REAL:: prop3
REAL:: prop4
...
INTEGER:: LTYPE
INTEGER:: ISETS
...
END MODULE element
As you can see, proel is an allocatable data structure that contains some information about the properties of the material, the element and so on. This data is taken from a text file through a I/O routine like
SUBROUTINE setdata
USE element
IMPLICIT NONE
ALLOCATE(PROEL(NSETS))
DO ISETS=1,NSETS
... ! here I am loading succefully the string variable STRVAR
! from the text file with data
LTYPE=GETVAL(STRVAR) ! here I retreive the value of LTYPE
PROEL(ISETS)%LTYPE=LTYPE ! here I save the (integer) variable for the set
IF(LTYPE.NE.0)THEN
CALL SETELM
END IF
END DO
END SUBROUTINE
Inside subroutine SETELM I am setting some particular parameters used in the program, but at some point I recall LTYPE saved in PROEL
SUBROUTINE SETELM
USE element
IMPLICIT NONE
LTYPE=PROEL(ISETS)%LTYPE
SELECT CASE(LTYPE)
CASE( 1)
...
CASE( 2)
...
CASE DEFAULT
...
END SELECT
END SUBROUTINE
Now, between the assignment LTYPE=PROEL(ISETS)%LTYPE and the SELECT CASE(LTYPE), the variable LTYPE changes value without any reason. The value is random (in the sense that I do not know where it comes from but it is not like uninitialized vars) and there are no others operations in the middle that work with LTYPE.
I am working with VS2008 and Fortran, so I turned on every warning, but neither compilation nor runtime give some possible hint.
Do you have any idea why is that happening?
Is there a way to call the field of a derived type via string argument in fortran?
something like...
subroutine set(car, fieldName, value)
type(Car_T) :: car
character*(*) :: fieldName
character*(*) :: value
car%[fieldName] = value
end subroutine set
I know you can do stuff like this in javascript, c#, ect., but this could really help me from having a ton of duplicate code if fortran allows it.
No. You will need to write the executable code (perhaps a SELECT CASE construct) that maps the value of the string across to the relevant component.
You only need to write this once for each unique set of component names.
You can do something similar with namelists but it is for items known to the program: not new items.
integer:: inin
real:: rere
namelist /info/ inin, rere
inin = 0 ! default
rere = 20.4 ! default
read (*, nml=info)
print *, 'inin=', inin
print *, 'rere=', rere
stop
end
On the input
&info inin=2 rere=40.0 /
Or if you wish to input one value only
&info rere=3.162 /
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)