Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Passing defined types to subroutines

kolber__Michael
New Contributor I
1,324 Views

Our company has an application that has been worked on for a long tine.  One of the last big changes was to create derived types that contain arrays of related data.  In fact we have types that are make up of other types that contain arrays.  Currently the size of the arrays is set using parameters.  We waist a lot of space doing  this as most of our clients do not have the amount of data that would come close to the limit.

What I am trying to do is allocate each array as we read the data.  This involves passing the derived type around between a number of subroutines.  I am just not sure what the correct way do this.  So far I have implemented this with one array,  I did have to remove it from its derived type to make it work.  To do this to all of the derived types would necessitate a complete rewriting of the code.

 

Thanks,

Michael

 

0 Kudos
18 Replies
FortranFan
Honored Contributor II
1,324 Views

@Michael,

Take a look at the new Fortran language org website https://fortran-lang.org/ as well as Fortran Wiki: http://fortranwiki.org/fortran/show/HomePage.

Using MODULEs in Fortran with their ability to facilitate explicit interfaces to subprogram (which is needed for assumed-shape arrays of any type) is something you will find useful for arrays of derived types: http://www.mathcs.emory.edu/~cheung/Courses/561/Syllabus/6-Fortran/struct.html

Also, take a look at parameterized derived types in Fortran for your application: https://software.intel.com/content/www/us/en/develop/documentation/fortran-compiler-developer-guide-and-reference/top/language-reference/data-types-constants-and-variables/derived-data-types/parameterized-derived-type-declarations.html

 

 

 

0 Kudos
kolber__Michael
New Contributor I
1,324 Views

I have no problems allocating an outer type as  follows

type x

     real*8 A

     integer*4 B

end type x

type y

   real*8  C

   real*8 d

end type y

 

type z

   type (x)   xx(10)

  type (y)   yy(20)

end type z

 

I have been able to allocate type z if I want an array of structures, but what I want is to allocate type x and type y, with only 1 type z existing.

 

Thanks.

0 Kudos
FortranFan
Honored Contributor II
1,324 Views

kolber, Michael wrote:

I have no problems allocating an outer type as  follows ..

I have been able to allocate type z if I want an array of structures, but what I want is to allocate type x and type y, with only 1 type z existing. ..

If you show small, fully worked out examples of what you have now and also your attempt at what does not work, readers can offer you better guidance.

You don't show any allocatable types at all in Quote #3 nor any allocations.

Your last sentence, "what I want is to allocate type x and type y, with only 1 type z existing," is unclear to me.  Is what is of interest to you the ability to create objects of type 'z' that have components 'xx' and 'yy' of different shapes than the hard-wired value of 20?  If so, have you looked at the ALLOCATABLE attribute?  See https://software.intel.com/content/www/us/en/develop/documentation/fortran-compiler-developer-guide-and-reference/top/language-reference/a-to-z-reference/a-to-b/allocatable.html

   type :: z
      type(x), allocatable :: xx(:)
      type(y), allocatable :: yy(:)
   end type
   ..
   type(z) :: foo, bar
   ..
   allocate( foo%xx(10), foo%yy(15) ) !<-- 1
   ..
   allocate( bar(xx(25), bar%yy(35) ) !<-- 2

The impression I have is you are not quite familiar with derived types in Fortran.  If you're keen to make progress and want to make good use arrays of objects of such derived types - all of which is extremely simple and has been so since Fortran 90 which was nearly 30 years ago, you will help yourself greatly by giving a little bit of structured time to review Fortran language that has advanced quite a bit since FORTRAN 77 e.g., real*8 was a non-standard extension used often with FORTRAN 77.   You can utilize online resources I mentioned in Quote #2 such as Fortran Wiki or better yet a textbook e.g., as https://www.amazon.com/FORTRAN-SCIENTISTS-ENGINEERS-Stephen-Chapman/dp/0073385891

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,324 Views

typo>>allocate( bar(xx(25), bar%yy(35) ) !<-- 2

allocate( bar%xx(25), bar%yy(35) ) !<-- 2

0 Kudos
kolber__Michael
New Contributor I
1,324 Views

This code is in an include file

      type b$$s
      sequence
         integer*4                nbs
         integer*4                new_nbs
         integer*4                nsw
         integer*4                swap_first_transfer
         integer*4                pbc_dates(mpbc_)
         real*4                   pbc_values(mpbc_)     
         character*32             series_label(ms_)       ! so string search can look at adjacent locations
         type (b$$series) , allocatable ::  series(:)
     !    type (b$$series)         series(ms_)
         type (b$$group)          group(mtbg_)
         type (b$$bond)           bond(mb_)
         type (b$$fee)            fee(mbfee_)
         type (b$$fee)            insur(mbfee_)
         type (b$$cost)           coi(mbfee_)
         type (b$$cost)           spread(mbfee_)
         type (b$$deposit)        deposit(mbfee_)
         type (override$_data)    over
      end type

      type (b$$s) b$s
 

The above code is in an include file that is included in every subroutine that uses b$s.

The structure is filled from reading a data file

        inum_series = getLength( bondFundPlacemarkList)
         allocate(b$s%series(inum_series))
         b$s%nbs = inum_series
I had added the allocate statement into the subroutine that fills in the data for that structure.  The program is a dll.  It compiles, but I get a run time error when it calls the dll. The error is a stack overflow.

I have done most of my programming in Fortran 77.  That is what this application is in, except before I joined the company that decided to create these data structure to replace individual arrays.  Calls to subroutines were getting rather lengthy.

 

Michael

 

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,324 Views

   type b$$s
     ...
   end type

   type (b$$s) b$s

>>The above code is in an include file that is included in every subroutine that uses b$s.

Then each subroutine contains its own copy of b$s.

Is that what you intended??

The use of INCLUDE file in this case in inadequate. Consider using a module instead:

! bDollarDollar.f90
module bDollarDollar

integer, parameter :: mpbc_ = ?? ! your value here
integer, parameter :: ms_ = ?? ! your value here
integer, parameter :: mtbg_ = ?? ! your value here
integer, parameter :: mb_ = ?? ! your value here
integer, parameter :: mbfee_ = ?? ! your value here

type b$$series
	...	! your type here
end type b$$series

type b$$group
	...	! your type here
end type b$$group

type b$$bond
	...	! your type here
end type b$$bond

type b$$fee
	...	! your type here
end type b$$fee

type b$$cost
	...	! your type here
end type b$$cost

type b$$deposit
	...	! your type here
end type b$$deposit

type override$_data
	...	! your type here
end type override$_data

type b$$s
	sequence
	integer*4                nbs
	integer*4                new_nbs
	integer*4                nsw
	integer*4                swap_first_transfer
	integer*4                pbc_dates(mpbc_)
	real*4                   pbc_values(mpbc_)     
	character*32             series_label(ms_)       ! so string search can look at adjacent locations
	type (b$$series) , allocatable ::  series(:)
!	type (b$$series)         series(ms_)
	type (b$$group)          group(mtbg_)
	type (b$$bond)           bond(mb_)
	type (b$$fee)            fee(mbfee_)
	type (b$$fee)            insur(mbfee_)
	type (b$$cost)           coi(mbfee_)
	type (b$$cost)           spread(mbfee_)
	type (b$$deposit)        deposit(mbfee_)
	type (override$_data)    over
end type b$$s

type (b$$s) b$s ! global to those procedures with "use bDollarDollar"
end module bDollarDollar

 

Then:

subroutine Foo()
    use bDollarDollar ! was INCLUDE "yourFileNameNere.inc"
    ...

Jim Dempsey

0 Kudos
kolber__Michael
New Contributor I
1,324 Views

Jim,

The structures have to be filled by one routine and then once filled are then used to provide the data for calculations in the application.  My idea was to read the data file, determine how many of each type exist and allocate the number of elements to what is needed.  Will that concept work with the above code.  Can I do the following for all the types

 

type(b$$Series), allocable  :: series(:)

For all of the different type arrays. If I set it up as a module can the data be shared.  I use modules to globalize counters and that does seem to work.

0 Kudos
Steve_Lionel
Honored Contributor III
1,324 Views

The correct way is certainly not to use an INCLUDE file - as Jim says, this creates an independent definition of the type in each program unit. The language does not consider these to be the "same type" when it comes to checking arguments.

Jim has the right approach - put all these definitions in a module, and then USE the module where needed. If you're lucky and the INCLUDE line is the first after the SUBROUTINE or FUNCTION, you can put the USE in the INCLUDE file and not have to change any code.

0 Kudos
kolber__Michael
New Contributor I
1,324 Views

Steve,

If the first subroutine called allocates the size of the type arrays will that allocation stay with the array so that the next subroutine that has the use statement will have the allocated array proper length.

 

0 Kudos
Steve_Lionel
Honored Contributor III
1,324 Views

The module that Jim suggests declares only the type, not any array, so I am not sure what you mean by "array length". If the type from the module is used everywhere, it will be the same size structure everywhere because it is the same structure.

If you want to allocate arrays, make sure that any routines you pass them to declare them with assumed-shape bounds (:). Then the shape of the array will be passed in.

0 Kudos
kolber__Michael
New Contributor I
1,324 Views

What I want to have happen is that the types in the modules start out as unallocated and get allocated by the subroutines that fill them in.  They have to return to the calling program as allocated so that when they are accessed later on the data from the files is in the arrays that each derived type defines.

0 Kudos
Steve_Lionel
Honored Contributor III
1,324 Views

Types don't get allocated. Variables do. You can declare ALLOCATABLE variables in the module, allocate them when you want and they will stay allocated.

0 Kudos
kolber__Michael
New Contributor I
1,324 Views

My predecessors decided to have arrays of types that are components of a larger type.  The arrays were sized with fixed parameters.  I am trying to change that to allocating the number of each type so that we have better control over the memory.

that is why they did this

type bb$s

type(bb$sSeries)  Series(ms_)

type(bb$sFees) Fees(mf_)

end type bb$s

 

The parameter are stored in a separate include file and the include file with the bb$s type is include where ever the type is used.  The programmers who wrote most of the original code that was just propagated were old Vax programmers who I was told followed the standards they learned at RPI. Some how I feel like I am working with code that might never be able to be modernized.

 

0 Kudos
FortranFan
Honored Contributor II
1,324 Views

kolber, Michael wrote:

..

The parameter are stored in a separate include file and the include file with the bb$s type is include where ever the type is used.  The programmers who wrote most of the original code that was just propagated were old Vax programmers who I was told followed the standards they learned at RPI. Some how I feel like I am working with code that might never be able to be modernized.

"Some how I feel like I am working with code that might never be able to be modernized." - if you are looking for a "quick answer" to your derived type issue, chances are any response will be highly suboptimal.  If you provide details with a working example and what changes you'd like to make to it. readers can provide useful guidance to resolve the matter but it may not be modern.  For the latter, in addition to what I have suggested above, you will help yourself considerably by also reviewing references in this Dr Fortran blog, almost every one of them has a section on code modernization: https://stevelionel.com/drfortran/2013/12/30/doctor-fortran-in-its-a-modern-fortran-world/

0 Kudos
mecej4
Honored Contributor III
1,324 Views

Mike Kolber: I think that you should pause and evaluate the whole situation in depth before continuing. Please read my private message regarding details. 

0 Kudos
John_Campbell
New Contributor II
1,324 Views

I am probably going to repeat a lot of what has been said before, but I think you need to review what is being achieved and where you want to get to.
Include files were typically used for COMMON to define groups of data plus provide a global address for this data.
Modules are a modern equivalent of COMMON, with the advantage of allocatable arrays for flexible storage and derived types to group data with a common name, eg element(el_num)%some_property(pr_num). I find there can be many of these (derived types), which are typically small. Changing from COMMON to MODULE can improve readability, but there are 2 big problems with adopting derived types; 1) there is lots of editing of the code and 2) the savings in memory use can be modest.
The issue of large arrays is slightly different. If the strategy you are looking at is to reduce program size, by reducing array sizes with ALLOCATE, this can be a separate issue from adopting derived types. Allocatable arrays are best (must be) placed in modules to give them global access so converting parts of COMMON to MODULE will be required and identifying where to place “USE module_name” will be needed.
When I adopted this transition in a large code, I started by providing a module and identifying where it is to be used. Then transiting each large array, one or two at a time, identifying where it should be created/allocated and then how it should be used. I started with the biggest arrays, to identify the benefits.
Don’t select too many changes at a time and hopefully you can adequately test each change as it is introduced. Just changing an array from having dimensions defined by parameters, to being an allocatable array does not involve significant editing of the code.
Essentially I am saying:
For phase 1 of your changes identify large arrays in COMMON and shift them to allocatable arrays in a module. Do it in small bits, so you learn more about modules with minimal editing of your code. Start with the largest array or large arrays that are not used extensively. Keep reviewing the benefits you are achieving.
For phase 2, when you are more familiar with modules, try introducing derived types, again a few at a time. This will involve many more edits to the code, so you need to audit for incorrect changes. This might not realise a lot of benefits so again review.
For phase 3, when you are truly brave, identify where you may benefit from “parameterized derived types”. These require significant code changes and may not be required for data structures in old style Fortran. They can be great for the right application, but for an old large code set, it is best to minimise the edits required.
Start small and make sure each change works.

The alternative approach is convert the code to 64-bit and ignore the array inefficiencies.

Start with a change that has minimal code changes. Every key stroke is a possible error, so there must be a benefit.

0 Kudos
LRaim
New Contributor I
1,324 Views

One may simply change any array defined in COMMON  into a pointer to the array (using the same name) so the programmer has not to change statements where the array is used.

I can also remark that, with reference to object oriented programming, the USE statement of modules, in Fortran subroutines or functions, destroys "encapsulation" which, in my opinion, is one of the most important features for robust software architectures.  

Regards

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,324 Views

John Campbell's post #17 is good advice.

One modification to his advice which I find quite handy is to add conditional compilation based on command line defined/not defined variables:

command line /DUSE_Allocate

!dir$ if(defined(USE_Allocate))
...
!dir$ else
...
!dir$ endif

The benefits are:

  • This self documents the changes
  • It provides a means to quickly revert back the edits in the event you find a discrepency in results

Then when completing all of conversions, you can then remove the conditionals and unexpanded secctions.

Note, use multiple USE_... defines such that you can incrementally introduce changes.

Jim Dempsey

0 Kudos
Reply