- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
@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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
typo>>allocate( bar(xx(25), bar%yy(35) ) !<-- 2
allocate( bar%xx(25), bar%yy(35) ) !<-- 2
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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/
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Mike Kolber: I think that you should pause and evaluate the whole situation in depth before continuing. Please read my private message regarding details.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page