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

Allocatable Array of Inherited Derived Types Issues in Fortran

Tripp_L_
Beginner
4,253 Views

I'm attempting to create global-ish-ly available allocatable array of a set of derived types that share inheritance with a single object. Fortran does not seem to make this very easy. The below is what I have so far.

First the derived types and module with the allocatable array.

    Module Environment

        use Entity_M
        type(Entity_C), dimenion(:), allocatable :: objects
    End Module Environment
    
    Module Entity_M
        type Entity_T
            integer :: id
            real*8 :: time
            real*8, dimension(3) :: currPos
    
            type(TrajectoryDatum), dimension(:), allocatable :: trajTable
    
        end type Entity_T
    
        type Entity_C
            class(Entity_T), pointer :: e
        end type Entity_C
    
        type, extends(Entity_T) :: Aircraft_T
            real*8 :: altitude
        end type Aircraft_T
    
        type, extends(Entity_T) :: Missile_T
            integer :: targetID
        end type Missile_T
    
    End Module Entity_M

Now the main program

   

 Program Main
    
        use Initialization
        use Environment
        use Entity_M
    
        call simInit(3)
        write(*,*) objects%trajTable !<---- this does not persist
    
        call runSim()
        
    End Program Main

The code with the issue

    

Module Initialization
    
        use Entity_M
    
        contains
    
        subroutine simInit(numOfObjects)
    
            integer, intent(in) :: numOfObjects
    
            call objectsInit(numOfObjects)
            call launchersInit()
    
        end subroutine simInit
    
    
        subroutine objectsInit(numOfObjects)
    
            use Environment
            
            integer, intent(in) :: numOfObjects
    
            !local
            type(Aircraft_T) :: aircraft
            integer :: i
    
            allocate(objects(numOfObjects)
    
            do i = 1, numOfObjects
    
                aircraft%trajTable = getTrajectoryData()
                
                call allocatePointer(objects(i)%e, aircraft)
                
            end do
    
        end subroutine objectsInit
    
        subroutine allocatePointer(c, t)
    
            class(Entity), pointer, intent(out) :: c
            type(Aircraft), target, intent(in) :: t
    
            c => t
    
        end subroutine allocatePointer
    
    End Module Initialization

This above just example code written on a computer that doesn't have a compiler. I did my best and hopefully if there are typos they are few. I did my best to mirror the structure of the original code.

The problem is that the field "objects%trajTable" goes back to a undefined pointer after it leaves the "objectsInit" subroutine. The other values like time, id, and currPos are still correct. How can I correct this?

I am using Visual Studio 2012 and Intel Visual Fortran 2015.

Here is a link to the stack overflow question which has slightly better formatting. http://stackoverflow.com/questions/31439117/allocatable-array-of-inherited-derived-types-issues-in-fortran

0 Kudos
1 Solution
IanH
Honored Contributor III
4,253 Views

Tripp L. wrote:

I am a little confused by asking why is the component a POINTER. I would love to avoid using the POINTER if possible or a potential better solution the the array of multiple inherited types, but the only help I've been able to come across is https://software.intel.com/en-us/forums/topic/280765?language=en-us&https=1 if there is better information I would be very interested in a link.

I searched for several hours looking for a solution, but I couldn't find what I was looking for.

Can the component that is currently a POINTER instead be an ALLOCATABLE component?  With some exceptions, as of Fortran 2003 only use pointers when you want "a thing" to simply be a reference to something else.  If "a thing" is a data object in its own right (it has its own value, rather than just reflecting the value of something else), then make it allocatable.

Fortran 95 code often used POINTER where in Fortran 2003 you should use ALLOCATABLE (because you were very limited with what you could do with ALLOCATABLEs in Fortran 95).  You occasionally see Fortran 2003 code being written with habits developed under Fortran 95.

    type Entity_c   !! container type     
! Can you change:                                          
!        class(Entity_t), pointer :: e
! to:
         class(Entity_t), allocatable :: e
    endtype

 

Beyond that, using a derived type container or wrapper is the Fortran way for creating an array of objects where the dynamic type (or other characteristics) may vary from element to element.  A fundamental aspect of the language is that within an array, you can only have variation in value from element to element, within an array all other array element characteristics are the same. 

With the derived type wrapper the characteristic that you otherwise want to vary becomes part of the value of an object of the container type (and hence part of the value of an element of an array of that container type). 

Things are perhaps more verbose than they otherwise could be, but along with the container type you typically provide procedures that simplify/assist in working with that type and arrays of that type.  Other language features, such as structure and array constructors can also help.

Fortran 2008 introduces intrinsic polymorphic assignment, which will also reduce some of the verbosity.

Module Entity_m
  implicit none

  integer, parameter :: rk = kind(1.0d0)
  
  type Entity_t   !! base type
      integer :: trajTable( 2 )
  endtype

  type, extends(Entity_t) :: Aircraft_t
      real(rk) :: altitude
  endtype

  type, extends(Entity_t) :: Missile_t  !! dangerous...
      integer :: targetID
  endtype

  type Entity_c   !! container type
      class(Entity_t), allocatable :: e
  endtype

  type(Entity_c), allocatable :: objects(:)
contains
  subroutine objectsInit( numObj )
    integer :: numObj
    integer :: i
    
    allocate( objects( numObj ) )
    do i = 1, numObj
      if ( mod( i, 2 ) == 1 ) then
        objects(i) = Entity_c(  &
            Aircraft_t(  &
              trajTable = i, &
              altitude = 10.0d0 * i ) )
      else
        objects(i) = Entity_c(  &
            Missile_t(  &
              trajTable = 10000 * i,  &
              targetID = -100 * i ) )
      endif
    enddo
  end subroutine
EndModule

 

View solution in original post

0 Kudos
15 Replies
FortranFan
Honored Contributor III
4,253 Views

As shown, I don't think your code will even compile.  Consider the first module Environment: there is no USE statement and compiler won't know anything about Entity_C.  So your question about object not persisting is probably not relevant at this stage.  Please get your code to compile and link first and post it fully here so readers can then provide help.

0 Kudos
Tripp_L_
Beginner
4,253 Views

First, the answer to the problem and then a comment about the state of this forum in response to the other post on this thread.

The answer for potential future readers, as explained here http://stackoverflow.com/questions/31439117/allocatable-array-of-inherited-derived-types-issues-in-fortran/31439949#31439949, is that I need to use the allocate internal function instead of  my allocatePointer subroutine. It is used in the form:

    allocate(object%e, source=aircraft)

It's precisely because of comments like this that I usually avoid this forum and just use StackOverflow. I decided to give it another chance today. It is full people who are more concerned with specifications than being helpful. I made a few changes that should fix the couple typos I had from writing it without being able to compile. Obviously since I'm telling you the problem I'm having and explained it is an example indicative of the problem I'm having, it is "relevant at this stage".

Once again, the code is on a classified machine and large amounts of the code are classified so I can't just move it onto another computer. Additionally, I don't have a compiler on my unclassified machine to check my typing. I'm sorry, but this is the best I could do given the circumstances, and it was obviously adequate since someone on stack overflow could spot my problem in only a few minutes. The couple of issues you pointed out are obvious typos and have nothing to do with the issue at hand.

0 Kudos
FortranFan
Honored Contributor III
4,253 Views

Tripp L. wrote:

.. a comment about the state of this forum in response to the other post on this thread. ..

It's precisely because of comments like this that I usually avoid this forum and just use StackOverflow. I decided to give it another chance today. It is full people who are more concerned with specifications than being helpful. .. someone on stack overflow could spot my problem in only a few minutes. The couple of issues you pointed out are obvious typos and have nothing to do with the issue at hand.

Note my intention is only to be helpful always, you can decide for yourself based on threads like these: https://software.intel.com/en-us/forums/topic/561699#new.  I don't understand your comments about the forum and how they arise based on this thread or are relevant to this thread. . 

0 Kudos
IanH
Honored Contributor III
4,253 Views

The different forums have their different natures, which lead to different strengths and weaknesses, but there is a pretty large overlap of users between them.  There's even someone running around on StackOverflow with a familiar sounding name, that uses an ancient picture of Dr Fortran as their avatar.  Clearly a fraud.

Technically a debugging type question on stackoverflow without a minimum working example is off-topic (note that the first comment to your question was pretty much along the lines of what FortranFan said).  Because the Fortran tag is relatively obscure such questions tend to survive, but in the more popular tags it is possible that the question would likely have been downvoted into oblivion.

Personally I find the constraints on discussion on StackOverflow crippling.  It is great for exchanging specific knowledge, but not so good for going beyond that (which requires discussion), and many questions raise issues that go beyond specifics.

The "original" problem with the code is actually a little different to that posited by the answer (but the answer deals with a later problem and the observed symptoms) - the dummy argument `t` with the TARGET attribute is argument associated with an actual argument `aircraft` that doesn't have the TARGET attribute.  This is permitted by the language, however pointers associated with the dummy argument become undefined when the procedure ends (F2008 12.5.2.4p11). 

This is a nasty sort of undefinition, in that it is hard to detect (things may continue to work under most circumstances).  Fortran 2008 improves (but doesn't completely fix) the situation by permitting INTENT(IN), POINTER dummy arguments to be associated with actual arguments that have the TARGET attribute, but I don't think ifort supports that yet.

The naive fix to this original problem (which would then compile with the Fortran 2008 improvement) is to add TARGET to the aircraft variable - but then you will run into the identified problem of pointers to unsaved local variables becoming undefined when going out of scope.

The accepted solution of using ALLOCATE to define the pointer suggests that the pointer component has value semantics.  Given the code clearly requires Fortran 2003, why is the component a POINTER?  Bar a few circumstances (some of which are due to the source robustness of TARGET dummy arguments discussed above), pointers should only be used in Fortran 2003 when reference semantics are required.

It can be helpful to always use the terminology "associated" for pointers and "allocated" for allocatables.  (The "override" of the ALLOCATABLE statement to associate a pointer with an anonymous target is a bit unfortunate in this regard, but that's the way things are.)  If you had this terminology separation, then the appearance of a subroutine that operates on pointers with "allocate" in its name; and the appearance of a subroutine with allocate in its name that doesn't do any allocation, may have raised questions.

0 Kudos
Tripp_L_
Beginner
4,253 Views

Thank you. That was very helpful.

I should have written in the question that I understood the problem of the reference going out of scope. The issue I was having more clearly is that I was unable to find a way to do what needed to be done, and that was as close as I could get. I am a little confused by asking why is the component a POINTER. I would love to avoid using the POINTER if possible or a potential better solution the the array of multiple inherited types, but the only help I've been able to come across is https://software.intel.com/en-us/forums/topic/280765?language=en-us&https=1 if there is better information I would be very interested in a link.

I searched for several hours looking for a solution, but I couldn't find what I was looking for. I imagine like you said that due to the fact two things that should have different names have the same name makes it hard to find.

I can't do much better than I did writing out the problem without a compiler on my machine. Even if I was transcribing the example directly from the computer with the compiler I doubt I would have had many fewer typos. I know it is not this forum's fault that I don't have a compiler on my computer with internet access at work, but I do feel I gave an adequate example to show the problem I was having. I guess if the general consensus is that I didn't, I apologize, and perhaps I do fit in better in another community.

 

0 Kudos
mecej4
Honored Contributor III
4,253 Views

Your working environment could be made more productive and comfortable by installing Gfortran on the networked computer. Any reason not to do that?

0 Kudos
John_Campbell
New Contributor II
4,253 Views

I found problems with the first code sample:

Module Environment

    use Entity_M
    type(Entity_C), dimenion(:), allocatable :: objects
End Module Environment

Module Entity_M

I am assuming that ifort is sequential, so won't module Environment get an old version of Entity_M, which is subsequently replaced. If there have been any changes made to Entity_M, what will happen ? If they were in separate files, then I assume that MAKE or a project build would fix this.

It looks wrong to have both module declarations out of order in the same file.

John

0 Kudos
Steven_L_Intel1
Employee
4,253 Views

Yes, that will indeed be an issue.

0 Kudos
mecej4
Honored Contributor III
4,253 Views

John is right about the ordering of module sources but, in this particular case, compiling the first code (module Environment) in #1 fails and no module file is produced at all, so the issue of stale module files does not arise.

I remember that a few years ago I was surprised when a Fortran compiler compared the creation time of module files with the modification time of source files and issued a warning message about certain modules being out of date. I do not remember which compiler did that.

0 Kudos
FortranFan
Honored Contributor III
4,253 Views

OP has made clear any problems in the first code example are of little interest.  OP was simply posting a code snippet from memory and the expectation was this forum readers will be able look past any issues and quickly spot the problem of interest to OP pertaining to persistence of an object.

By the way, do Intel Forums now offer the option to edit the original post?  I'm quite certain the first post when I looked at it was different from what is there now e.g., the first module i.e., Environment did not include any USE statements. 

0 Kudos
IanH
Honored Contributor III
4,254 Views

Tripp L. wrote:

I am a little confused by asking why is the component a POINTER. I would love to avoid using the POINTER if possible or a potential better solution the the array of multiple inherited types, but the only help I've been able to come across is https://software.intel.com/en-us/forums/topic/280765?language=en-us&https=1 if there is better information I would be very interested in a link.

I searched for several hours looking for a solution, but I couldn't find what I was looking for.

Can the component that is currently a POINTER instead be an ALLOCATABLE component?  With some exceptions, as of Fortran 2003 only use pointers when you want "a thing" to simply be a reference to something else.  If "a thing" is a data object in its own right (it has its own value, rather than just reflecting the value of something else), then make it allocatable.

Fortran 95 code often used POINTER where in Fortran 2003 you should use ALLOCATABLE (because you were very limited with what you could do with ALLOCATABLEs in Fortran 95).  You occasionally see Fortran 2003 code being written with habits developed under Fortran 95.

    type Entity_c   !! container type     
! Can you change:                                          
!        class(Entity_t), pointer :: e
! to:
         class(Entity_t), allocatable :: e
    endtype

 

Beyond that, using a derived type container or wrapper is the Fortran way for creating an array of objects where the dynamic type (or other characteristics) may vary from element to element.  A fundamental aspect of the language is that within an array, you can only have variation in value from element to element, within an array all other array element characteristics are the same. 

With the derived type wrapper the characteristic that you otherwise want to vary becomes part of the value of an object of the container type (and hence part of the value of an element of an array of that container type). 

Things are perhaps more verbose than they otherwise could be, but along with the container type you typically provide procedures that simplify/assist in working with that type and arrays of that type.  Other language features, such as structure and array constructors can also help.

Fortran 2008 introduces intrinsic polymorphic assignment, which will also reduce some of the verbosity.

Module Entity_m
  implicit none

  integer, parameter :: rk = kind(1.0d0)
  
  type Entity_t   !! base type
      integer :: trajTable( 2 )
  endtype

  type, extends(Entity_t) :: Aircraft_t
      real(rk) :: altitude
  endtype

  type, extends(Entity_t) :: Missile_t  !! dangerous...
      integer :: targetID
  endtype

  type Entity_c   !! container type
      class(Entity_t), allocatable :: e
  endtype

  type(Entity_c), allocatable :: objects(:)
contains
  subroutine objectsInit( numObj )
    integer :: numObj
    integer :: i
    
    allocate( objects( numObj ) )
    do i = 1, numObj
      if ( mod( i, 2 ) == 1 ) then
        objects(i) = Entity_c(  &
            Aircraft_t(  &
              trajTable = i, &
              altitude = 10.0d0 * i ) )
      else
        objects(i) = Entity_c(  &
            Missile_t(  &
              trajTable = 10000 * i,  &
              targetID = -100 * i ) )
      endif
    enddo
  end subroutine
EndModule

 

0 Kudos
Tripp_L_
Beginner
4,253 Views

@Ian H. Thank you. I appreciate the reply. I was unaware I could use allocatable in that context. I will be switch where relevant in my code. I was also unaware that Fortran had automatic-esque constructors.

Is there a good location for examples of the new features of Fortran code online or a particularly good book? In the year and a half I've been working with Fortran I haven't come across anything I thought was particularly helpful across a broad range of problems. I've come across the GFortran, Intel Fortran, and I want to say an HP api, but those only seem particularly helpful as reference when you are just looking for a refresher on a particular code example. The rest of everything I have found is most just in old tutorials posted for college courses.

I also purchased Fortran 95/2003 explained, but while it is a perfectly fine written book it has been nowhere near as helpful to be as something like JavaScript the Good Parts, or the Deitel Books for actually learning a language.

I was aware of the ability to change default kind, but so far I have left it to be compatible with some old code I haven't touched yet. That was definitely on my list of things to do.

Was the "!! dangerous" comment a reference to something I was doing incorrectly programmatically, or a joke about missiles? Sorry, I'm just trying to get all the help I can while I have someone who knows more than me about the Fortran language available to talk to as I do not have that with my current employer.

@mecej4 Installing gfortran on my unclassified machine is a good idea. Thank you for the suggestion, I will be asking IT about it this week. Are there any major pitfalls of using that in comparison to Intel Fortran?

@Everyone else. Those two modules in the first part are not actually in the same file. I didn't think I had to break them up on here as an example. And yes I have edited the original post, as I mentioned in previous comments, to make it have fewer typos. It was never my intention to have sloppy work, I was, once again, doing the best I could with what I had to work with. I wasn't transcribing from memory either, I was summarizing the problem area of a couple thousand lines of code in a way that was readable by someone in a short period of time and didn't have classified information in it.

 

0 Kudos
mecej4
Honored Contributor III
4,253 Views

Tripp L. wrote:

Are there any major pitfalls of using that in comparison to Intel Fortran?

Not many compared to the pitfalls that come with not having any compiler on your computer. Gfortran is free and of good quality, and I assume that you may not want to purchase Intel Fortran for your personal machine.

The compiler options, of course, are completely different, as are the non-standard extensions. That may be an advantage if you seek to make your code standard and portable.

0 Kudos
Steven_L_Intel1
Employee
4,253 Views

If you have a commercial, named-user license for Intel Fortran, you may install it on multiple computers as long as you are the only user of the product on those computers.

0 Kudos
Tripp_L_
Beginner
4,253 Views

Thanks. I'll ask IT about that too.

0 Kudos
Reply