4

How do I convert r(i,j) to a 1D array so that I can sort the number easily?

program sort
  implicit none
  character CN*8,O*7
  integer j,iconf,nconf
  integer i,nbins,t
  integer n,nmax,ind,num,b
  parameter (n=216)
  double precision xbox,rq
  parameter (nmax=3091,nconf=1)
  double precision atom(nmax),id(nmax),ox(nmax),oy(nmax),oz(nmax)
  double precision xij,yij,zij,rij
  double precision r(n,n),A(n)
  open(unit=10,status='unknown',file='1000.gro')
   do iconf= 1,nconf
    write(*,*)iconf
     read(10,*)
     read(10,*)
   do i=1,n
     read(10,'(A8,A7,1i5,3f8.3)')CN,O,num,ox(i),oy(i),oz(i)
   enddo
     read(10,*)xbox        ! read the xbox for PBC

  open(unit=3,file='dist.txt')

   do i=1,n
    do j=1,n
   if(i .ne. j) then
   xij=ox(i)-ox(j)
   yij=oy(i)-oy(j)
   zij=oz(i)-oz(j)
   r(i,j)=dsqrt(xij**2 + yij**2 + zij**2)
      write(3,'(i3,2x,i3,4x,f17.15)') i,j, r(i,j)
    endif 
    enddo
    enddo
    enddo
    END

I have to calculate the distance and save this in array as r (i,j). I want to convert r(i,j) to a 1 dimensional array, so that I can sort the r(i,j)easily.

5
  • 3
    Well, it's ALMOST F77... Only the DO..END DO isn't in F77. But, yeah, it makes no sense to be asking about F77 nowadays. Commented Aug 18, 2018 at 22:06
  • 3
    And the free format, implicit none, and the exclamation mark for a comment Commented Aug 18, 2018 at 22:18
  • The input data here is ox, oy, and oz. You can also well decide to store the distances r in a 1d array of length n*(n-1)/2 indexed as "(i-1)*n+j" (where i is the smallest of the two indices). Commented Aug 19, 2018 at 9:43
  • 1
    Reshape is an option too, but best to write a little routine that works on the two dimensional array, since the underlying data is intrinsically two dimensional in nature. But most likely rather than sorting Claudia wants to rank or index the array rather than sort it, so she preserves which pairs of indices are at what distance. But that is out of the remit of stackoverflow Commented Aug 19, 2018 at 10:22
  • @lan Bush if you can help me in sorting of 2d array which retains the i, j and only sorting of r(i,j) Commented Aug 22, 2018 at 18:17

5 Answers 5

6

Two previous answers have looked at the literal question of how to "convert" a rank-2 array in to a rank-1 array. They use different approaches:

  • copy the values in to a new rank-1 array;
  • in the end subroutine, have a rank-1 dummy argument associated with a rank-2 actual argument.

We can expand on both of these approaches.

reshape in its simplest form returns the desired rank-1 array. As an alternative, we can say that an array constructor, of the form [x] does that also:

real r1d(6), r2d(3,2)
r2d=5.
r1d = [r2d]

When the actual argument is an array element the array dummy argument and those elements of the array including and following the actual argument are in sequence association.

With sequence association, the dummy array would be assumed-size or explicit shape. As in this case we're interested in the whole array, we could just pass the whole array:

real r2d(3,2)
call sub(r2d)

where

subroutine sub(r1d)
  real r1d(*) ! or r1d(6), etc.
end subroutine

The important thing here is that for explicit-shape and assumed-size dummy arguments the rank need not match that of the actual argument.

As noted before, this first group of approaches involves creating a new array. The use of sequence association doesn't. A third approach is also available: having a rank-1 pointer pointing to a rank-2 target. Creating a copy does also mean, of course, that any changes aren't reflected in the original rank-2 array. Sequence association and pointers will see changes passed on.

More detail on any one of these things can be found in other questions and answers.

Whether, for sorting, it makes much sense to treat a rank-2 array as a rank-1 array is a different consideration.

Sign up to request clarification or add additional context in comments.

2 Comments

How do you control the ordering of element during the transformation from 2D to 1D ? The simple [] or reshape gives elements gives the array in column first order. How to get this in row first order.
For a 2-D array one can use transpose to get an intermediate array with elements in the other order; or in an array constructor a more complicated expression such as [(a(i,:),i=1,n)]. Of course, if doing this a copy is a likely result.
4

This seems tailor made for the reshape function: https://gcc.gnu.org/onlinedocs/gfortran/RESHAPE.html In this tiny example, reshape is used first to make a two-dimensional array A, then reshape is called again to make the one-dimension array C.

Program reshape_demo
    use, intrinsic :: iso_c_binding

    implicit none
    real(kind=c_float),allocatable :: A(:,:),C(:)
    integer(kind=c_int) :: krow
    allocate(A(3,3))
    A=reshape((/1,2,3,4,5,6,7,8,9/),(/3,3/))
    do krow=1,3
       write(*,fmt="(1p3e10.3)")A(krow,:)
    end do
    C=reshape(A,(/9/))
    write(*,fmt="(9(1x,f4.1))")C
End Program reshape_demo

2 Comments

This makes a copy of the array - probably not what is wanted. In fact, it might even make two copies in the process.
also, a more aesthetic modern Fortran array constructor would be square brackets [], instead of `(//).
4

You can pass r(1,1) to a subroutine that declares the argument to be a one-dimension array. This is legal in Fortran (with some restrictions that don't apply to your code) and uses a feature called "sequence association".

1 Comment

This is an advanced and efficient feature like the pointer in C.
0

By doing that you will lose all information about the pairs to which the distance corresponds to but if it's just the distances you want then I don't get all the convoluted answers. Why not just do:

[....]

    implicit none
    real(8), dimension(:), allocatable:: dist_arr

    allocate(dist_arr(n**2))

    k=0
    do i=1,n
        do j=1,n
            k=k+1
            dist_arr(k)=r(i,j)
        enddo
    enddo

[....]

    

4 Comments

Do you mean for dist_arr to have elements in order different from r? (So that dist_arr(2) matches r(1,2) instead of r(2,1).) If you do, then perhaps it's worth saying that to avoid any later confusion.
@francescalus The question is not clearly stated but from from what I can gather they just want to have a descending/ascending list of distances. In that case they don't care for any correlations during the transformation since they will sort this 1D array anyway. So in the end, what you want is to "untangle" the table into a straight line (1D array). You can go row by row or column by column, it doesn't matter.
If the order doesn't matter, then why would we prefer this approach over the arguably much simpler dist_arr=[r]?
@francescalus I honestly didn't know that existed. In all my years of building fortran programs I always prefer the "long way round" instead of ready built functions, mainly because I want to be able to fine-tune it later if I want. But sure, as I explained above, I think they just want an easy 1D array to sort instead of the 2D table so this should work fine.
0

Nowadays, there are not so many FORTRAN users, so the questions are not so frequently seen. The accepted answer is great and can be formed into a simple function as Solution 1. But there is another option to use C_F_POINTER without making copy of the source array (can gain more efficiency with huge array).

Solution 1:

function downsize(A)result(C)
 real::A(:,:)
 real,allocatable::C(:)
 C=reshape(A,[size(A)])
end function downsize

To use the function

real,allocatable::C(:); C=downsize(A);

Solution 2:
C_F_POINTER in ISO Binding can be used to transfer the memory address like in C.

USE, INTRINSIC :: ISO_C_BINDING
!EDIT...
integer::i,i1d(9)=[(i,i=1,9)]; !reshape((real([(i,i=1,9)])),[9])
integer,dimension(:,:),pointer::p2d
! Here has to be "Deferred-shape" but it only defined a pointer to an array, not an array of pointers as is always misleading...
!EDIT...
real::r2d(3,3)=reshape([(i*1.0,i=1,9)],[3,3])
real, pointer ::pr1d(:)
!Let's Grow the size from I1D to I2D by the pointer pI2D...
call c_f_pointer(c_loc(i1d),pi2d,[3,3])
!The pointer pi2d is a 2d array with the contents of i1d. Note you cannot deallocate the pointer pi2d - any deallocation has to be of pointee i1d.
!Let's Down the size from R2D to R1D by the pointer pR1D ...
call c_f_pointer(c_loc(r2d), pr1d,[size(r2d)])

4 Comments

There are not too many FORTRAN users indesd. We have been using Fortran in the past three decades instead (well, I am not that old, I've onle been using it for less than two decades). In Solution 2 I would just use Fortran pointer remapping.
i1d(9)=reshape((real([(i,i=1,9)])),[9]) is a very complicated way to write i1d(9)=[(i,i=1,9)], isn't it? Either way, this example code is not valid Fortran because i1d is not a valid argument to c_loc. (And as Vladimir F points out, is there a compelling reason to use C addresses when Fortran pointers directly would do the same thing?)
Thanks to francescalus! Yes, I agree. i1d was copied from r1d. But I cannot see why i1d is not valid for c_loc.
c_loc(i1d) isn't allowed unless i1d is a pointer or a target.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.