Search code examples
arraysfortranderived-types

Array of derived type: select entry


Currently in my code I have a 2D array

integer, allocatable :: elements(:,:)

and define some constants

integer, parameter :: TYP = 1
integer, parameter :: WIDTH = 2
integer, parameter :: HEIGHT = 3
! ...
integer, parameter :: NUM_ENTRIES = 10

and allocate something like

allocate(elements(NUM_ENTRIES,10000))

so I can access elements like

write(*,*) elements(WIDTH,100) ! gives the width of the 100th element

Now I would like to have not only integer but a mixture of types for every element. So I define a derived type

type Element
    logical active
    integer type
    real width
    ! etc
end type

and use an array of Elements

type(Element), allocatable :: elements(:)

With the 2d array version I could call a subroutine telling it which entry to use. E.g.

subroutine find_average(entry, avg)
    integer, intent(in) :: entry   
    real, intent(out) :: avg
    integer i, 
    real s

    s = 0
    do i = lbound(elements,1), ubound(elements,1)
        if (elements(TYP,i) .gt. 0) s = s + elements(entry,i)
    end do
    avg = s/(ubound(elements,1)-lbound(elements,1))
end subroutine       

So I could call find_average(HEIGHT) to find the average height or pass WIDTH to get the average width. (And my subroutines do more advanced things than finding the average height or width, this is just an example.)

Question: How can I use different types (as with the derived type) but also reuse my functions to work with different entries (as in the example subroutine)?


Solution

  • For the array case, instead of passing in arguments array and index i, you could pass in the single argument array (i). When you switch to having the derived type, similarly you could pass in variable_of_type % element rather than passing in the entire variable_of_type and somehow instructing the procedure which subelement it is supposed to work on. If the code needs to be different for the different types of elements (e.g., logical, integer, real), then you could write specific procedures for each, but call then with a common name via a generic interface block. The compiler has to be able to distinguish the procedures of the generic interface block by some characteristic of the arguments, here their type. For a code example in which the distinguishing characteristic is array rank see how to write wrapper for 'allocate'

    EDIT: example code. does this do what you want?

    module my_subs
    
       implicit none
    
       interface my_sum
          module procedure sum_real, sum_int
       end interface my_sum
    
    contains
    
    subroutine sum_real (array, tot)
       real, dimension(:), intent (in) :: array
       real, intent (out) :: tot
       integer :: i
    
       tot = 1.0
       do i=1, size (array)
          tot = tot * array (i)
       end do
    end subroutine sum_real
    
    subroutine sum_int (array, tot)
       integer, dimension(:), intent (in) :: array
       integer, intent (out) :: tot
       integer :: i
    
       tot = 0
       do i=1, size (array)
          tot = tot + array (i)
       end do
    end subroutine sum_int
    
    end module my_subs
    
    
    program test_dt
    
    use my_subs
    
    implicit none
    
    type my_type
       integer weight
       real length
    end type my_type
    
    type (my_type), dimension (:), allocatable :: people
    type (my_type) :: answer
    
    allocate (people (2))
    
    people (1) % weight = 1
    people (1) % length = 1.0
    people (2) % weight = 2
    people (2) % length = 2.0
    
    call my_sum ( people (:) % weight, answer % weight )
    write (*, *)  answer % weight
    
    call my_sum ( people (:) % length, answer % length )
    write (*, *)  answer % length
    
    end program test_dt