How to add new element to dynamical array in Fortran 90

11,297

Solution 1

I suspect, looking at an artefact, that you noticed the problem - but quickly moved on.

The suspicious line, to me is:

    allocate(clist(isize+2))

Why isn't the new size isize+1? I guess that you tried that, but then the program failed.

Seeing why the program failed (possibly crashed) is key to why you aren't getting the correct result. Look closely at the loop (print statement removed for clarity).

do i=1,isize
    clist(i) = list(i)
end do
clist(i+1) = element

You want to say "copy all elements from list to clist, then append element". Which is correct. However

do i=1,isize
    clist(i) = list(i)
end do
! Here, i=isize+1
clist(i+1) = element
! Which means
! clist(isize+2) = element.

In summary, after the loop the loop index variable doesn't have the value it had in the final iteration.

Solution 2

I know this question is very old, but I recently had to build such a subroutine, and I found out that, starting from Fortran2003, there is a beautiful one liner:

    SUBROUTINE append_int(vec, val)
    !***********************************************************************
    !> \brief Appends val in vec if not already present
    !> \date 05 2020
    !***********************************************************************
    INTEGER, DIMENSION(:), ALLOCATABLE, INTENT(INOUT)    :: vec
    INTEGER, INTENT(IN)                                  :: val

    ! Remove this test if you don't mind not having unique values
    IF (.NOT. ANY(vec .EQ. val)) THEN
        vec = [vec, val]
    END IF

    END SUBROUTINE

Your array will be automatically reallocated at the appropriate size. Plus, you can generate equivalent routines for Reals, for appending an array to another ... and then wrap all of them in an interface, so that you always can call the same subroutine whatever the type of your data.

Share:
11,297
chanfort
Author by

chanfort

Updated on June 14, 2022

Comments

  • chanfort
    chanfort almost 2 years

    I need to use dynamical arrays in Fortran 90 for cases when I can't predict exact size of array initially. So I wrote a code, which should expand allocatable array each time new element is added to the end of array:

      subroutine DArray()
    
      double precision, dimension(:), allocatable :: list
    
      allocate(list(1))
    
      list(1) = 1.1
    
      call AddToList(list, 2.2)
      call AddToList(list, 3.2)
      call AddToList(list, 4.2)
      call AddToList(list, 5.2)
    
      print *, list(1)
      print *, list(2)
      print *, list(3)
      print *, list(4)
      print *, list(5)
    
    
      end
    
    
    
      subroutine AddToList(list, element)
    
      double precision :: element
      double precision, dimension(:), allocatable :: list
      double precision, dimension(:), allocatable :: clist
    
      if(allocated(list)) then
        isize = size(list)
        allocate(clist(isize+1))
        do i=1,isize
            clist(i) = list(i)
        end do
        clist(i+1) = element
    
        deallocate(list)
        allocate(list(isize+1))
    
        do i=1,isize+1
            list(i) = clist(i)
        end do
    
        deallocate(clist)
    
      end if
    
    
      end
    

    So does anyone see if I missing something here?


    Solved by francescalus.

    Working code for double precision dynamical arrays is:

      module DynamicalArrays
    
      contains
    
          subroutine AddToList(list, element)
    
              IMPLICIT NONE
    
              integer :: i, isize
              double precision, intent(in) :: element
              double precision, dimension(:), allocatable, intent(inout) :: list
              double precision, dimension(:), allocatable :: clist
    
    
              if(allocated(list)) then
                  isize = size(list)
                  allocate(clist(isize+1))
                  do i=1,isize          
                  clist(i) = list(i)
                  end do
                  clist(isize+1) = element
    
                  deallocate(list)
                  call move_alloc(clist, list)
    
              else
                  allocate(list(1))
                  list(1) = element
              end if
    
    
          end subroutine AddToList
    
    
      end module DynamicalArrays
    

    The demo subroutine, from which array can be filled would be:

      subroutine UserDArrayTest()
    
      use DynamicalArrays
    
    
      integer :: i
      double precision, dimension(:), allocatable :: list
      double precision :: temp
    
      temp = 0.1
      do i=1,10
        temp = temp+1
        call AddToList(list, temp)
      end do
    
      do i=1,10
        print *, i, list(i)
      end do
    
    
      end
    

    Note that it's best to keep module code in the separate file, but I also find out that it works when module code is above main program and subroutine codes.

  • chanfort
    chanfort over 9 years
    Oh, wow, it works now very nicely with a correction of clist(isize+1) = element (I didn't know that in fortran last index after end do becomes isize+1). P.S. Do you know if it would be possible to avoid 6 lines of these interfaces at the beginning of each subroutine, where I want to use dynamical arrays? Maybe it's possible somehow to put them upon USE statement and modules?
  • francescalus
    francescalus over 9 years
    A module would be my preferred (over interface blocks) way of providing the explicit interface, and examples will be easy to find searching here. And as we've moved on to code recommendations, I'll also bring up the move_alloc intrinsic. And perhaps even considering linked lists rather than dynamic resizing.
  • chanfort
    chanfort over 9 years
    Thanks a lot. I just added module corrections and now things looks nicer. There is also correction that array does not need to be allocated initially and move_alloc. I used changes on the top of question that other users could easily find the complete code. Let's actually keep linked lists for another story :)
  • Vladimir F Героям слава
    Vladimir F Героям слава almost 4 years
    Yes, it is probably useful to have it here even when the question requested Fortran 90. We do have that in the more generic questions and answers though stackoverflow.com/questions/38758216/…
  • francescalus
    francescalus almost 4 years
    Eventually this will surprise someone who finds that the lower bound of an array has changed after appending an element.
  • Herman Toothrot
    Herman Toothrot over 3 years
    @francescalus what do you mean? This procedure is not safe?
  • francescalus
    francescalus over 3 years
    @HermanToothrot, the procedure is "safe", but it potentially changes things in unexpected ways. Consider allocate (vec(0:5), source=[1,2,3,4,5,6]); print*, lbound(vec); call append_int(vec, 6); print*, lbound(vec); call append_int(vec, 7); print*, lbound(vec).