Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
Announcements
This community is designed for sharing of public information. Please do not share Intel or third-party confidential information here.
27160 Discussions

User Defined I/O -- derived type variable with many components

Kevin_McGrattan
918 Views

I have a number of derived types in my code with many (dozens) of components, some of which are allocatable. I am writing the user-defined subroutines for unformatted reading and writing. Is there a way that I can indicate that I want all components of the variable written out or read in without having to list the components explicitly. For example, if this is my write routine

subroutine wall_write(w,unit,iostat,iomsg)
   class(wall_type), intent(in) :: w
   integer, intent(in) :: unit
   integer, intent(out) :: iostat
   character(*), intent(inout) :: iomsg
   write(unit=unit,iostat=iostat,iomsg=iomsg) w%i,w%j,w%k,w%itmp
end subroutine wall_write

is there a way to indicate that I want to write out everything, i.e. i, j, k, itmp, without explicitly listing them. This is a simple case, but I have dozens and potentially hundreds of components.

0 Kudos
1 Solution
FortranFan
Honored Contributor II
871 Views

@Kevin_McGrattan ,

To the best of my knowledge, there is no ready facility in the Fortran standard for what you seek.

However, since your question comes across to me to as general to the language and nothing specific to Intel Fortran, I suggest you also try to discuss them at the Fortran Discourse site (https://fortran-lang.discourse.group/), comp.lang.fortran, etc.  That is, it may help you to engage with the broader community on Fortran and get multiple feedback.  In additions, if you have any ideas and/or would like to make a case for language improvements, please consider add your thoughts in an issue thread at https://github.com/j3-fortran/fortran_proposals.

In the meantime, if you really have "dozens and potentially hundreds of components," the thought that comes immediately to my mind is to employ a workaround involving a combination of intrinsic derived-type IO plus user-defined IO i.e., do object-oriented based analysis and design (OOA, OOD) to refactor your "class" design (derived type per Fortran jargon) as appropriate - it may involve refactoring - and do classification of "data" in your class and use type composition and type extension as suitable.

Say you 'i', 'j', 'k', and 'itmp' "data" belong to `foo_t' and your "wall type" is either an extension of 'foo_t' or has a component of such a type named 'foo'. your IO statement as part of the subroutine toward user-defined IO for `wall_type' can simply be

   write(unit=unit,iostat=iostat,iomsg=iomsg) w%foo ! or w%foo_t

where you needn't necessarily require introduction of defined IO in that foo_t, the intrinsic facility can do the heavy-lifting for you.

Thus you may be able to greatly reduce the verbosity in your IO routines at the cost of reorganization and refactoring elsewhere.

View solution in original post

7 Replies
FortranFan
Honored Contributor II
872 Views

@Kevin_McGrattan ,

To the best of my knowledge, there is no ready facility in the Fortran standard for what you seek.

However, since your question comes across to me to as general to the language and nothing specific to Intel Fortran, I suggest you also try to discuss them at the Fortran Discourse site (https://fortran-lang.discourse.group/), comp.lang.fortran, etc.  That is, it may help you to engage with the broader community on Fortran and get multiple feedback.  In additions, if you have any ideas and/or would like to make a case for language improvements, please consider add your thoughts in an issue thread at https://github.com/j3-fortran/fortran_proposals.

In the meantime, if you really have "dozens and potentially hundreds of components," the thought that comes immediately to my mind is to employ a workaround involving a combination of intrinsic derived-type IO plus user-defined IO i.e., do object-oriented based analysis and design (OOA, OOD) to refactor your "class" design (derived type per Fortran jargon) as appropriate - it may involve refactoring - and do classification of "data" in your class and use type composition and type extension as suitable.

Say you 'i', 'j', 'k', and 'itmp' "data" belong to `foo_t' and your "wall type" is either an extension of 'foo_t' or has a component of such a type named 'foo'. your IO statement as part of the subroutine toward user-defined IO for `wall_type' can simply be

   write(unit=unit,iostat=iostat,iomsg=iomsg) w%foo ! or w%foo_t

where you needn't necessarily require introduction of defined IO in that foo_t, the intrinsic facility can do the heavy-lifting for you.

Thus you may be able to greatly reduce the verbosity in your IO routines at the cost of reorganization and refactoring elsewhere.

Kevin_McGrattan
823 Views

Thanks for the link. I'll try it.

 

As for your suggestion, I worry about making the sub-sub components of this derived type more complicated. I suppose I could lump all the scalar components together into one derived type, and then list the allocatable components one by one. But I was wondering if there's some way to automatically list the components in the order that they are listed in the type declaration. Sort of like the SEQUENCE feature. I often add to and subtract from the component list, and I would prefer that these components only be listed in the type declaration, as opposed to both the read and write subroutines.

Kevin_McGrattan
717 Views

Thanks for the info. A follow-up comment. I should have said that there are two reasons for "writing" and "reading" a complex derived type variable. One is for stopping and restarting a computational fluid dynamics code that can run for days. The second is to enable MPI sends and recv's of the derived type variable. There are techniques to "pack" Fortran derived types into continuous memory for MPI purposes, but I found that the easiest way to do both the I/O and MPI exchange is to just literally write out everything and read in everything component by component. 

jimdempseyatthecove
Black Belt
633 Views

Unfortunately there isn't a "do what I want" type of write of a (complex UDT) as each individual can have a different preference of "what I want".

>>One is for stopping and restarting a computational fluid dynamics code that can run for days. 

What I've done is to write explicit save and restore routines (which could have been UDT formatted writes, but I chose to write as explicit subroutines). To aid in the process during development when fields (member variables) could be added or removed:

Use SEQUENCE in  the TYPE (at front)

Place large scalars (e.g. REAL(8), or REAL(16)) first, with first variable named FirstReal8, and last variable as LastReal8, follow with First Integer8,...,LastInteger8, FirstReal,...,LastReal,FirstInteger,...,LastInteger,FirstLogical,...,LastLogical,FirstChar,...,LastChar.

The non-allocatable arrays can be placed between the First... and Last...

Then the allocatable arrays and UDT's (without the First.../Last...)

The scalars with fixed arrays can be written by using the LOC(Last...)-LOC(First...) to get the count then dumped as blobs, one blob for each type.

The allocatables will have to be handled individually as well as the UDT's.

 

This does require that the layout of the type not change.

 

In the Implementation I used, I also added a mapping/conversion table to permit type conversions e.g. REAL(8) to REAL(4), INTEGER to INTEGER(1), etc... and even xxx to NULL. This provided a means to compress the data as well as to omit saving/restoring temporary/transient values.

Jim Dempsey

Kevin_McGrattan
581 Views

That's clever. I'll give it a try, thanks.

jimdempseyatthecove
Black Belt
566 Views

I am sorry that I cannot provide sample code as I am away from my development system at this time. There will be a fair amount of work for you to do, but this saves a lot of time as you do not need to capture individual scalars (and fixed arrays). The allocatables have to be captured individually (using lbound and ubound).

Jim Dempsey

 

Kevin_McGrattan
544 Views

No problem. I think I understand the basic concept.

Reply