0

I'm new to Fortran and I already have a hard time understanding the concept of broadcasting of arrays in Python, so it is even more difficult for me to implement it in Fortran

Fortran code:

program test
    implicit none
    integer,parameter::N=6,t_size=500
    real,dimension(t_size,N,2)::array1
    
    contains

    pure function f(a) result(dr)
    real::dr(N,N,2)
    real,intent(in)::a(N,2)
    real::b(N,N,2)
    real::c(N,N,2)
    b=spread(a,1,N)
    c=spread(a,2,N)
    dr=c-b

    end function f

end program

Now N would be the number of points and t_size is just the number of different time steps. I came up with this function which uses spread in two different dimensions to create a NxNx2 array.

Now I thought of using a line like for example r=f(array1(1,:,:) in order to get an array which holds all differences of spatial coordinates of every 2 points.

I already wrote code that does this in Python (taken from a physics textbook for Python)

r = np.empty((t.size, N, 2))
r[0] = r0

def f(r):
    dr=r.reshape(N,1,2)-r

where I can write later for example f(r[i]. (In this case, I left the line r[0] = r0, because it shows that an initial condition is given - later I plan to do this in Fortran by using the random_number subroutine.)

Now I hope it is clear what my question is. If anyone has a better idea (which I am sure there is) to implement broadcasting in Fortran, please let me know.

Please have a little patience with someone new to Fortran and also programming in general - thanks in advance for your replies.

I already tried it with the random_number subroutine and it worked, but I have no way of checking if the output is true.

1
  • 2
    The Fortran way is to write loops. Unlike interpreted python compiled Fortran Loops are good and compiled loops are fast. Learn to love loops. Commented Mar 29, 2024 at 17:13

1 Answer 1

0

Now I've got a bit more time I'll turn my comment into an answer.

Fortran is a compiled language. Python is an interpreted language. This leads to many differences, but one of the big ones is that in Fortran loops are fast, as they have been turned into optimized machine code, and more often than not are the preferred way to express what you want to do. However in Python loops are slow as they have to be (re-)interpreted every step of the way - hence the encouragement to use (to my mind) cryptic single liners invoking optimized functions from the python library. This "vectorisation" is rarely if ever needed in Fortran for simple operations - just write it (to my mind) the simple way with loops.

While I am here I will also mention if you do want performance you have your array indices the wrong way around - you want the one that you change most often ( 1:2 in the above ) to be the first index. This is ultimately to do with the way Fortran organizes it arrays in memory, if interested the key term to look into is "column major"

Finally I will mention the Fortran way is usually to use Subroutines rather than Functions. This is mostly stylistic, but does lead to a few simplifications I won't go into here.

Anyway putting the above together I would write it something like the below

ijb@ijb-Latitude-5410:~/work/stack$ cat not_python.f90
Program test

  Implicit None

  Real, Dimension( :, :, : ), Allocatable :: array1
  Real, Dimension( :, :, : ), Allocatable :: dr
  
  Integer :: d, N, t_size
  Integer :: t_step
  Integer :: id, iN, it
  
  Write( *, * ) 'N, t_size ?'
  Read ( *, * )  N, t_size

  d = 2

  Allocate( array1( 1:d, 1:N, 1:t_size ) )

  Do it = 1, Size( array1, Dim = 3 )
     Do iN = 1, Size( array1, Dim = 2 )
        Do id = 1, Size( array1, Dim = 1 )
           array1( id, iN, it ) = id + 10.0 * in + 100.0 * it
        End Do
     End Do
  End Do

  Allocate( dr( 1:d, 1:n, 1:n ) )

  t_step = 1
  Call f( array1( :, :, t_step ), dr )

  Call printit( dr )
  

Contains

  Pure Subroutine f( a, dr )

    Implicit None

    Real, Dimension(    :, : ), Intent( In    ) :: a
    Real, Dimension( :, :, : ), Intent(   Out ) :: dr

    Integer :: N
    Integer :: i, j
    
    N = Size( a, Dim = 2 )

    ! Could take advantage of symmetry here, dr( j, i ) = - dr( i, j )
    ! but probably gaines you little 
    Do j = 1, N
       Do i = 1, N
          dr( :, i, j ) = a( :, i ) - a( :, j )
       End Do
    End Do

  End Subroutine f

  Subroutine printit( dr )

    Use iso_fortran_env, Only : stdout => output_unit

    Implicit None
    
    Real, Dimension( :, :, : ), Intent( In ) :: dr

    Integer :: it, iN, id
    
    Do it = 1, Size( dr, Dim = 3 )
       Do iN = 1, Size( dr, Dim = 2 )
          Do id = 1, Size( dr, Dim = 1 )
             Write( stdout, '( 3( i2, 1x ), 6x, f5.1 )' ) &
                  id, iN, it, dr( id, iN, it )
          End Do
       End Do
    End Do

  End Subroutine printit

End Program test
ijb@ijb-Latitude-5410:~/work/stack$ gfortran -Wall -Wextra -fcheck=all -O -std=f2018 not_python.f90 -o not_python
ijb@ijb-Latitude-5410:~/work/stack$ ./not_python 
 N, t_size ?
3 2
 1  1  1         0.0
 2  1  1         0.0
 1  2  1        10.0
 2  2  1        10.0
 1  3  1        20.0
 2  3  1        20.0
 1  1  2       -10.0
 2  1  2       -10.0
 1  2  2         0.0
 2  2  2         0.0
 1  3  2        10.0
 2  3  2        10.0
 1  1  3       -20.0
 2  1  3       -20.0
 1  2  3       -10.0
 2  2  3       -10.0
 1  3  3         0.0
 2  3  3         0.0
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much for your answer! It helped me to implement a functioning loop - my learning material says to use functions unless you really have to use a subroutine, but I'll try your suggestion. Again, thank you!

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.