So far we've been reading HDF file and data set attributes using hdfed and inspecting various records more or less at random. In this section I'll show you how to do that from within your Fortran program, so that you can, for example, pass it on to a Gnuplot command file and, ultimately, print on your data plot.
Before going any further, let us run our program hdf-5 that reads an HDF file first. When we do so this time, we get the following output:
gustav@blanc:../src 12:51:28 !551 $ ./hdf-5 File SDS.hdf opened sd_file_id = 393216 Obtained information about the file file_info_status = 0 n_datasets = 1 n_file_attributes = 1 First scientific data set selected sd_set_id = 262144 Obtained information about the data set sd_set_name = SDStmpl rank = 1 dim_sizes = 10 data_type = 24 n_set_attributes = 1 data (before) = 10*0 sd_read_status = 0 data (after) = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 sd_set_status = 0 sd_file_status = 0 gustav@blanc:../src 12:51:30 !552 $This time the program finds one file attribute and one set attribute associated with the first data set.
Now we will modify program hdf-5, renaming it to hdf-8, so that it will read both file and data set attributes.
The following listing gets quite long, but there is little in it that is earth-shaking. It's long, because it has a lot of write statements. I have indented those so as to make them a little less prominent
program read_SDS use hdf; use dffunc; use netcdf; implicit none integer, parameter :: sd_set_index = 0, file_attr_index = 0, & set_attr_index = 0 integer, dimension(1), parameter :: start = (/ 0 /), stride = (/ 1 /) integer :: sd_file_id, sd_set_id, sd_read_status, sd_set_status, & sd_file_status, n_datasets, n_file_attributes, file_info_status, & rank, data_type, n_set_attributes, i, set_info_status, & file_attr_status, file_attr_type, file_attr_length, read_attr_status, & set_attr_status, set_attr_type, set_attr_length integer(kind=4), dimension(:), allocatable :: data integer, dimension(MAXVDIMS) :: dim_sizes integer, dimension(1) :: edges character(len=MAXNCNAM) :: sd_set_name = " ", file_attr_name = " ", & file_attribute = " ", set_attr_name = " ", set_attribute = " " sd_file_id = sfstart("SDS.hdf", DFACC_READ) write (*, *) "File SDS.hdf opened" write (*, *) "sd_file_id = ", sd_file_id file_info_status = sffinfo(sd_file_id, n_datasets, n_file_attributes) write (*, *) "Obtained information about the file" write (*, *) "file_info_status = ", file_info_status write (*, *) "n_datasets = ", n_datasets write (*, *) "n_file_attributes = ", n_file_attributes file_attr_status = sfgainfo(sd_file_id, file_attr_index, file_attr_name, & file_attr_type, file_attr_length) write (*, *) "file_attr_name = ", & file_attr_name(:len_trim(file_attr_name)) write (*, *) "file_attr_type = ", file_attr_type write (*, *) "file_attr_length = ", file_attr_length if (file_attr_length .le. MAXNCNAM & .and. file_attr_type .eq. DFNT_CHAR8) then read_attr_status = sfrcatt(sd_file_id, file_attr_index, file_attribute) write (*, *) "file_attribute = ", & file_attribute(:len_trim(file_attribute)) end if sd_set_id = sfselect(sd_file_id, sd_set_index) write (*, *) "First scientific data set selected" write (*, *) "sd_set_id = ", sd_set_id set_info_status = sfginfo(sd_set_id, sd_set_name, rank, dim_sizes, & data_type, n_set_attributes) write (*, *) "Obtained information about the data set" write (*, *) "sd_set_name = ", sd_set_name(:len_trim(sd_set_name)) write (*, *) "rank = ", rank write (*, *) "dim_sizes = ", (/ (dim_sizes(i), i = 1, rank) /) write (*, *) "data_type = ", data_type write (*, *) "n_set_attributes = ", n_set_attributes set_attr_status = sfgainfo(sd_set_id, set_attr_index, set_attr_name, & set_attr_type, set_attr_length) write (*, *) "set_attr_name = ", & set_attr_name(:len_trim(set_attr_name)) write (*, *) "set_attr_type = ", set_attr_type write (*, *) "set_attr_length = ", set_attr_length if (set_attr_length .le. MAXNCNAM & .and. set_attr_type .eq. DFNT_CHAR8) then read_attr_status = sfrcatt(sd_set_id, set_attr_index, set_attribute) write (*, *) "set_attribute = ", & set_attribute(:len_trim(set_attribute)) end if if (rank .eq. 1) then allocate (data(dim_sizes(1))); data = 0; edges = dim_sizes(1) write (*, *) "data (before) = ", data sd_read_status = sfrdata(sd_set_id, start, stride, edges, data) write (*, *) "sd_read_status = ", sd_read_status write (*, *) "data (after) = ", data deallocate(data) else write (*, *) "rank different from 1, skipping the data reading part" end if sd_set_status = sfendacc(sd_set_id) write (*, *) "sd_set_status = ", sd_set_status sd_file_status = sfend(sd_file_id) write (*, *) "sd_file_status = ", sd_file_status end program read_SDSBefore analysing the program let us compile and run it first:
gustav@blanc:../src 13:52:38 !582 $ make hdf-8 f90 -c -M/afs/ovpit.indiana.edu/@sys/HDF/modules hdf-8.f90 f90 -o hdf-8 hdf-8.o -L/afs/ovpit.indiana.edu/@sys/HDF/lib \ -lmfhdf -lnsl -ldf -ljpeg -lz -lm gustav@blanc:../src 13:52:43 !583 $ ./hdf-8 File SDS.hdf opened sd_file_id = 393216 Obtained information about the file file_info_status = 0 n_datasets = 1 n_file_attributes = 1 file_attr_name = File Contents file_attr_type = 4 file_attr_length = 30 file_attribute = Data for the chi^2-fit Program First scientific data set selected sd_set_id = 262144 Obtained information about the data set sd_set_name = SDStmpl rank = 1 dim_sizes = 10 data_type = 24 n_set_attributes = 1 set_attr_name = First Set Annotation set_attr_type = 4 set_attr_length = 25 set_attribute = redshift velocity in km/h data (before) = 10*0 sd_read_status = 0 data (after) = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 sd_set_status = 0 sd_file_status = 0 gustav@blanc:../src 13:52:46 !584 $As you see it all works just fine. We get information about the file, its scientific data sets, annotations and all that.
How did we get here? Let us now have a look at the program itself.
It begins the same way as before, until we get to the following:
file_attr_status = sfgainfo(sd_file_id, file_attr_index, file_attr_name, & file_attr_type, file_attr_length) write (*, *) "file_attr_name = ", & file_attr_name(:len_trim(file_attr_name)) write (*, *) "file_attr_type = ", file_attr_type write (*, *) "file_attr_length = ", file_attr_lengthFunction sfgainfo takes two arguments in:
file_attr_index, which correspond, in this case, to the file object and to the number of the first file attribute (here it is zero). In general any HDF file object, including the file itself, can have more than one attribute.
The function returns the name of
the attribute in
file_attr_name. We must reserve an appropriate
amount of space for the name, but we know that it can't be longer
MAXNCNAM, and so
file_attr_name is defined to
be of that length. When we declare that variable we also fill it
entirely with spaces (this is like filling C strings with nulls).
You must not assume that Fortran run-time
system will do that for you. It doesn't have to!
When we write the value of
file_attr_name on standard output
we trim it by calling function
This function returns
the real length of the string, but it works on space padded strings
only. So this is a yet another reason why we have pre-padded
the string with spaces.
The next value returned by function
file_attr_type. This is the type of data that constitutes
the attribute. As you can see the number returned is 4, which
The last type is the length of the attribute, in other words the length of the string. We could use it to allocate a required amount of space dynamically, but for teaching purposes alone it would make this program too messy. So all that I do at this stage is merely to check that I have enough space to read the data and then read it by calling function sfrcatt:
if (file_attr_length .le. MAXNCNAM & .and. file_attr_type .eq. DFNT_CHAR8) then read_attr_status = sfrcatt(sd_file_id, file_attr_index, file_attribute) write (*, *) "file_attribute = ", & file_attribute(:len_trim(file_attribute)) end ifObserve that I check if the type of data is indeed a character too. It doesn't have to be. For example, an attribute can be an array of numbers. But in that case you would have to use a different function to read it. That function is
sfrcatt has a very simple synopsis. It takes an object
ID as its first argument. It can be a file object or a data set object.
The second argument is an attribute indes, i.e., its number. Again,
we have only one attribute here, so its number is zero. The last
argument is what
sfrcatt writes the data on. In our case
this is a string of a predefined length
MAXNCNAM. That is
why I had to check that the string is long enough to take in all
the data. If it wasn't I would have to forgo reading the attribute,
or I would have to allocate required amount of memory dynamically.
Observe that when we write the content of
standard output, we trim it by calling function
Having obtained the file attribute we proceed as before to select
the first (and only) scientific data set and to read information
about it, including, this time, information about its attributes
and the attributes themselves. This is done in the same way as
with the file attributes, the only difference being the use of
sd_set_id in place of