Routine to read a generic table of data 
Author Message
 Routine to read a generic table of data

I have a fortran 90 application that reads in tables of data from many
different files. I would like a single routine to do this, because for each
file I do the same things - make sure the file exists, open the file, read
the data into an array, close the file, provide error handling, etc. I can
pass to the routine the needed information, like the text file path, the
number of records, the number of fields. However, my problem is that each
table has a different set of data types for fields in the table. For
example, I have a number of derived data types such as the following:

MODULE DataDefs
:
    TYPE Loan
        CHARACTER(10) LoanNumber
        REAL Rate
        INTEGER NumMonths
        REAL Amount
    END TYPE Loan
:
END MODULE DataDefs

All of my derived data types fit this same pattern - specifically, a series
of some number of intrinsic-typed variables.

I currently read in this data with something like the following:

:
USE DataDefs
TYPE(Loan) Loans(50)
CHARACTER(50) LoanDataFile
:
nLoans = 50
LoanDataFile = "Loans.txt"
CALL ReadTable1(Loans, nLoans, LoanDataFile)
:

The subroutine that reads in the data, which is assumed to be in a delimited
file, looks something like the following:

SUBROUTINE ReadTable1(Table, nRecords, FilePath)
    USE DataDefs
    INTEGER,  INTENT(IN)   :: nRecords
    CHARACTER(50), INTENT(IN) :: FilePath
    TYPE(Loan),  INTENT(OUT), DIMENSION(nRecords) :: Table
:
    OPEN(UNIT=99, STATUS='OLD', FILE=FilePath)
:
    DO i = 1, nRecords
        READ(99,*) Table(i)
    END DO
:
END SUBROUTINE ReadTable1

This works fine for me. However, if I want to do this for other data tables,
the subroutine is exactly the same, with the exception of one line: the type
declaration statement. My question is, how can I generalize this to work
with any derived data type, so that I don't have 20 copies of basically the
same subroutine?

Any help that anyone can give me would be GREATLY appreciated!!!!

Thanks,

Sean Geraghty



Sat, 23 Apr 2005 22:35:53 GMT  
 Routine to read a generic table of data


 My question is, how can I generalize this to work

Quote:
> with any derived data type, so that I don't have 20 copies of basically
the
> same subroutine?

> Any help that anyone can give me would be GREATLY appreciated!!!!

Basically, you can't. However, it might ease your maintenance task somewhat
if you replace the invariant code of each copy of the read routine by an
include line, and keep just one copy of that code in the corresponding
include file.

Regards,

Mike Metcalf



Sat, 23 Apr 2005 23:24:12 GMT  
 Routine to read a generic table of data


! File: ReadTable.i90
! Change the following line:
!> SUBROUTINE ReadTable1(Table, nRecords, FilePath)
 SUBROUTINE ReadTable(Table, nRecords, FilePath)

Quote:
>     USE DataDefs
>     INTEGER,  INTENT(IN)   :: nRecords
>     CHARACTER(50), INTENT(IN) :: FilePath

! Change the following line:
!>     TYPE(Loan),  INTENT(OUT), DIMENSION(nRecords) :: Table
     TYPE(MyType),  INTENT(OUT), DIMENSION(nRecords) :: Table
Quote:
> :
>     OPEN(UNIT=99, STATUS='OLD', FILE=FilePath)
> :
>     DO i = 1, nRecords
>         READ(99,*) Table(i)
>     END DO
> :

! Change the following line:
!> END SUBROUTINE ReadTable1
 END SUBROUTINE ReadTable
! End of file: ReadTable.i90

! File ReadTable.f90
module mod_1
   use DataDefs, MyType => Loan
   implicit none
   private MyType
   contains
include ReadTable.i90
end module mod_1

! module mod_2
! &c.

module generic_recombination
   use DataDefs
   use mod_1, only: ReadTable1 => ReadTable
!  use mod_2, only: ReadTable2 => ReadTable
!  &c.
   interface ReadTable
      module procedure ReadTable1
!     module procedure ReadTable2
!     &c.
   end interface ReadTable
end module generic_recombination
! End of file: ReadTable.f90



Sun, 24 Apr 2005 06:35:13 GMT  
 Routine to read a generic table of data
Thanks! This is useful, I will definitely give it a try.

Also, thanks to M. Metcalf for his reply.

Sean Geraghty



Quote:



> ! File: ReadTable.i90
> ! Change the following line:
> !> SUBROUTINE ReadTable1(Table, nRecords, FilePath)
>  SUBROUTINE ReadTable(Table, nRecords, FilePath)
> >     USE DataDefs
> >     INTEGER,  INTENT(IN)   :: nRecords
> >     CHARACTER(50), INTENT(IN) :: FilePath
> ! Change the following line:
> !>     TYPE(Loan),  INTENT(OUT), DIMENSION(nRecords) :: Table
>      TYPE(MyType),  INTENT(OUT), DIMENSION(nRecords) :: Table
> > :
> >     OPEN(UNIT=99, STATUS='OLD', FILE=FilePath)
> > :
> >     DO i = 1, nRecords
> >         READ(99,*) Table(i)
> >     END DO
> > :
> ! Change the following line:
> !> END SUBROUTINE ReadTable1
>  END SUBROUTINE ReadTable
> ! End of file: ReadTable.i90

> ! File ReadTable.f90
> module mod_1
>    use DataDefs, MyType => Loan
>    implicit none
>    private MyType
>    contains
> include ReadTable.i90
> end module mod_1

> ! module mod_2
> ! &c.

> module generic_recombination
>    use DataDefs
>    use mod_1, only: ReadTable1 => ReadTable
> !  use mod_2, only: ReadTable2 => ReadTable
> !  &c.
>    interface ReadTable
>       module procedure ReadTable1
> !     module procedure ReadTable2
> !     &c.
>    end interface ReadTable
> end module generic_recombination
> ! End of file: ReadTable.f90



Sun, 24 Apr 2005 12:42:15 GMT  
 Routine to read a generic table of data
Essentially you are looking for something like the templates of C++.
The solution by james is indeed very interesting.
I was thinking to something more dirty using cpp preprocessor:

module t1_mod
  type t1
    real :: a
  end type t1
  interface gensub
    module procedure my_gensub
  end interface
contains
#define TIPO t1
#include "gensub.F"
end module t1_mod

where gensub.F will be something like

subroutine my_gensub(a)
implicit none
type(TIPO) :: a
write(*,*) a
end subroutine my_gensub

in order to compile you first have to run the cpp preprocessor
and then compile

cpp -P t1_mod.F > t1_mod.tmp.f90
f90 -c t1_mod.tmp.f90
mv t1_mod.tmp.o t1_mod.o

giovanni



Sun, 24 Apr 2005 15:52:48 GMT  
 Routine to read a generic table of data
As an alternative to the James solution: Split the file operation from
the data conversion.

 SUBROUTINE ReadTable1(Table, nRecords, FilePath)
     USE DataDefs
     INTEGER,  INTENT(IN)   :: nRecords
     CHARACTER(*), INTENT(IN) :: FilePath
     TYPE(Loan),  INTENT(OUT), DIMENSION(nRecords) :: Table
     character (100) :: Records(nRecords)
     call LoadFromFile(Records,FilePath)
     do i = 1, nRecords
        read(Records(i),*) Table(i)
     enddo
     return
     end

! Repeat for other types ...

 SUBROUTINE ReadTable2(Table, nRecords, FilePath)
     ...

! LoadFromFile()

subroutine LoadFromFile(Lines,FilePath)
   character(*), intent(out) :: Lines(1:)
   character(*), intent(in)  :: FilePath

   open(...)

   do i = 1, size(Lines)
      read(99, '(a)', iostat=k) Lines(i)
      if (k /= 0) ...
   enddo
   close(...)
   return
   end

Quote:
>  OPEN(UNIT=99, STATUS='OLD', FILE=FilePath)
> :
>     DO i = 1, nRecords
>         READ(99,*) Table(i)
>     END DO
> :
> END SUBROUTINE ReadTable1

> I have a Fortran 90 application that reads in tables of data from many
> different files. I would like a single routine to do this, because for each
> file I do the same things - make sure the file exists, open the file, read
> the data into an array, close the file, provide error handling, etc. I can
> pass to the routine the needed information, like the text file path, the
> number of records, the number of fields. However, my problem is that each
> table has a different set of data types for fields in the table. For
> example, I have a number of derived data types such as the following:

> MODULE DataDefs
> :
>     TYPE Loan
>         CHARACTER(10) LoanNumber
>         REAL Rate
>         INTEGER NumMonths
>         REAL Amount
>     END TYPE Loan
> :
> END MODULE DataDefs

> All of my derived data types fit this same pattern - specifically, a series
> of some number of intrinsic-typed variables.

> I currently read in this data with something like the following:

> :
> USE DataDefs
> TYPE(Loan) Loans(50)
> CHARACTER(50) LoanDataFile
> :
> nLoans = 50
> LoanDataFile = "Loans.txt"
> CALL ReadTable1(Loans, nLoans, LoanDataFile)
> :

> The subroutine that reads in the data, which is assumed to be in a delimited
> file, looks something like the following:

> SUBROUTINE ReadTable1(Table, nRecords, FilePath)
>     USE DataDefs
>     INTEGER,  INTENT(IN)   :: nRecords
>     CHARACTER(50), INTENT(IN) :: FilePath
>     TYPE(Loan),  INTENT(OUT), DIMENSION(nRecords) :: Table
> :
>  OPEN(UNIT=99, STATUS='OLD', FILE=FilePath)
> :
>     DO i = 1, nRecords
>         READ(99,*) Table(i)
>     END DO
> :
> END SUBROUTINE ReadTable1

> This works fine for me. However, if I want to do this for other data tables,
> the subroutine is exactly the same, with the exception of one line: the type
> declaration statement. My question is, how can I generalize this to work
> with any derived data type, so that I don't have 20 copies of basically the
> same subroutine?

> Any help that anyone can give me would be GREATLY appreciated!!!!

> Thanks,

> Sean Geraghty



Sun, 24 Apr 2005 20:31:57 GMT  
 Routine to read a generic table of data
Hello,

The text/copy mechanism of coco was implemented
to provide a "poor man's template" to Fortran.  See:
http://users.erols.com/dnagle/coco.html
for more information.

--
Cheers!

Dan Nagle
Purple Sage Computing Solutions, Inc.

On Wed, 06 Nov 2002 08:52:48 +0100, Giovanni Cangiani

Quote:

>Essentially you are looking for something like the templates of C++.
>The solution by james is indeed very interesting.
>I was thinking to something more dirty using cpp preprocessor:

>module t1_mod
>  type t1
>    real :: a
>  end type t1
>  interface gensub
>    module procedure my_gensub
>  end interface
>contains
>#define TIPO t1
>#include "gensub.F"
>end module t1_mod

>where gensub.F will be something like

>subroutine my_gensub(a)
>implicit none
>type(TIPO) :: a
>write(*,*) a
>end subroutine my_gensub

>in order to compile you first have to run the cpp preprocessor
>and then compile

>cpp -P t1_mod.F > t1_mod.tmp.f90
>f90 -c t1_mod.tmp.f90
>mv t1_mod.tmp.o t1_mod.o

>giovanni



Mon, 25 Apr 2005 00:32:41 GMT  
 Routine to read a generic table of data
Sean,

I would recommend splitting the file open and the file read into two
routines.  I think that it will make the error handling easier.

Have you thought about using namelist I/O?  I was fortunate to work at a
company that had a namelist routine that was flexible enough to read all
variable types by means of preprocessing the Fortran source to replace the
namelist with a list (it created a map of the variables in each namelist) so
that a set of subroutines (named READNL, WRITNA, ...)could then reference
the map and fill in the values for I/O.  These routines were easy for the
novice programmer to use because they offered easy, consistent error
handling.  I did a quick net search and turned up no such preprocessor or
routines, but I recall some early efforts to add namelist to Fortrans that
had no namelist feature.  Maybe someone else can make a suggestion along
these lines?

Mark


Quote:
> I have a Fortran 90 application that reads in tables of data from many
> different files. I would like a single routine to do this, because for
each
> file I do the same things - make sure the file exists, open the file, read
> the data into an array, close the file, provide error handling, etc. I can
> pass to the routine the needed information, like the text file path, the
> number of records, the number of fields. However, my problem is that each
> table has a different set of data types for fields in the table. For
> example, I have a number of derived data types such as the following:

> MODULE DataDefs
> :
>     TYPE Loan
>         CHARACTER(10) LoanNumber
>         REAL Rate
>         INTEGER NumMonths
>         REAL Amount
>     END TYPE Loan
> :
> END MODULE DataDefs

> All of my derived data types fit this same pattern - specifically, a
series
> of some number of intrinsic-typed variables.

> I currently read in this data with something like the following:

> :
> USE DataDefs
> TYPE(Loan) Loans(50)
> CHARACTER(50) LoanDataFile
> :
> nLoans = 50
> LoanDataFile = "Loans.txt"
> CALL ReadTable1(Loans, nLoans, LoanDataFile)
> :

> The subroutine that reads in the data, which is assumed to be in a
delimited
> file, looks something like the following:

> SUBROUTINE ReadTable1(Table, nRecords, FilePath)
>     USE DataDefs
>     INTEGER,  INTENT(IN)   :: nRecords
>     CHARACTER(50), INTENT(IN) :: FilePath
>     TYPE(Loan),  INTENT(OUT), DIMENSION(nRecords) :: Table
> :
>     OPEN(UNIT=99, STATUS='OLD', FILE=FilePath)
> :
>     DO i = 1, nRecords
>         READ(99,*) Table(i)
>     END DO
> :
> END SUBROUTINE ReadTable1

> This works fine for me. However, if I want to do this for other data
tables,
> the subroutine is exactly the same, with the exception of one line: the
type
> declaration statement. My question is, how can I generalize this to work
> with any derived data type, so that I don't have 20 copies of basically
the
> same subroutine?

> Any help that anyone can give me would be GREATLY appreciated!!!!

> Thanks,

> Sean Geraghty



Mon, 25 Apr 2005 02:52:31 GMT  
 Routine to read a generic table of data
Thanks for the suggestion - very interesting approach. It appears to have
the advantage of segmenting the code to minimize code duplication, but at
the expense of doubling the amount of memory required to hold the data.
Would you agree? My application could potentially read in very large
tables - tens of millions of records - so memory could be an issue.

Thanks again,

Sean


Quote:
> As an alternative to the James solution: Split the file operation from
> the data conversion.

>  SUBROUTINE ReadTable1(Table, nRecords, FilePath)
>      USE DataDefs
>      INTEGER,  INTENT(IN)   :: nRecords
>      CHARACTER(*), INTENT(IN) :: FilePath
>      TYPE(Loan),  INTENT(OUT), DIMENSION(nRecords) :: Table
>      character (100) :: Records(nRecords)
>      call LoadFromFile(Records,FilePath)
>      do i = 1, nRecords
>         read(Records(i),*) Table(i)
>      enddo
>      return
>      end

> ! Repeat for other types ...

>  SUBROUTINE ReadTable2(Table, nRecords, FilePath)
>      ...

> ! LoadFromFile()

> subroutine LoadFromFile(Lines,FilePath)
>    character(*), intent(out) :: Lines(1:)
>    character(*), intent(in)  :: FilePath

>    open(...)

>    do i = 1, size(Lines)
>       read(99, '(a)', iostat=k) Lines(i)
>       if (k /= 0) ...
>    enddo
>    close(...)
>    return
>    end

> >  OPEN(UNIT=99, STATUS='OLD', FILE=FilePath)
> > :
> >     DO i = 1, nRecords
> >         READ(99,*) Table(i)
> >     END DO
> > :
> > END SUBROUTINE ReadTable1




- Show quoted text -

Quote:
> > I have a Fortran 90 application that reads in tables of data from many
> > different files. I would like a single routine to do this, because for
each
> > file I do the same things - make sure the file exists, open the file,
read
> > the data into an array, close the file, provide error handling, etc. I
can
> > pass to the routine the needed information, like the text file path, the
> > number of records, the number of fields. However, my problem is that
each
> > table has a different set of data types for fields in the table. For
> > example, I have a number of derived data types such as the following:

> > MODULE DataDefs
> > :
> >     TYPE Loan
> >         CHARACTER(10) LoanNumber
> >         REAL Rate
> >         INTEGER NumMonths
> >         REAL Amount
> >     END TYPE Loan
> > :
> > END MODULE DataDefs

> > All of my derived data types fit this same pattern - specifically, a
series
> > of some number of intrinsic-typed variables.

> > I currently read in this data with something like the following:

> > :
> > USE DataDefs
> > TYPE(Loan) Loans(50)
> > CHARACTER(50) LoanDataFile
> > :
> > nLoans = 50
> > LoanDataFile = "Loans.txt"
> > CALL ReadTable1(Loans, nLoans, LoanDataFile)
> > :

> > The subroutine that reads in the data, which is assumed to be in a
delimited
> > file, looks something like the following:

> > SUBROUTINE ReadTable1(Table, nRecords, FilePath)
> >     USE DataDefs
> >     INTEGER,  INTENT(IN)   :: nRecords
> >     CHARACTER(50), INTENT(IN) :: FilePath
> >     TYPE(Loan),  INTENT(OUT), DIMENSION(nRecords) :: Table
> > :
> >  OPEN(UNIT=99, STATUS='OLD', FILE=FilePath)
> > :
> >     DO i = 1, nRecords
> >         READ(99,*) Table(i)
> >     END DO
> > :
> > END SUBROUTINE ReadTable1

> > This works fine for me. However, if I want to do this for other data
tables,
> > the subroutine is exactly the same, with the exception of one line: the
type
> > declaration statement. My question is, how can I generalize this to work
> > with any derived data type, so that I don't have 20 copies of basically
the
> > same subroutine?

> > Any help that anyone can give me would be GREATLY appreciated!!!!

> > Thanks,

> > Sean Geraghty



Mon, 25 Apr 2005 12:45:09 GMT  
 
 [ 9 post ] 

 Relevant Pages 

1. ? F90 routines for n-D integral with table data

2. Generic Table -- Hash Table Implementation Module

3. Problems reading data out of an Access Query or Table

4. Reading tables of numerical data with Python

5. ada routine reading c data problem

6. Tested routine for simulting external read of ascii data (for omission detection)

7. Converting data from TPS table to MS SQL 7 table getting wrong values

8. generic retreive routine

9. Generic routines (with F-bounds) for Eiffel users

10. A generic error routine and a question.

11. Generic Table -- Other necessary modules

12. Generic Table -- Example of usage

 

 
Powered by phpBB® Forum Software