3

I'm working on trying to pass an array into a function to be able to calculate new values that will replace the original values in that array. However, I keep getting zeroes to be returned and I'm not sure why. My code is below:

program HW10
    implicit none
    integer :: i
    integer, parameter :: &
    p=38      !lines to read
    real, parameter :: &
    g=9.81    !Value of gravity acceleration
    integer , dimension(p) :: direction, speed, rh, speedconv
    real, dimension (p) :: pressure, height, temp, dewpt, mixr
    real :: average, knots

    open(1,file='HW10input.txt', status='old', action='read')

    10 format (F6.1, T9, F6.1, T16, F5.1, T23, F5.1, T33, I2, T38, F4.2, T46, I3, T53, I3)

    do i=1,p
        read(1,10)pressure(i), height(i), temp(i), dewpt(i), rh(i), mixr(i), direction(i), speed(i)
    end do


    close (1)

    open(2, file='outputfilehw10.txt', status='new', action='write') 
    do i=1,p
    write (*, 20) pressure(i), height(i), temp(i), dewpt(i), rh(i), mixr(i), direction(i), speed(i)
    20 format (F6.1, T9, F6.1, T16, F5.1, T23, F5.1, T33, I2, T38, F4.2, T46, I3, T53, I3)
    end do
    write (*,*) 'Average= ', average(temp, p)
    do i=1,p
        write (*,*) 'Wind Speeds: ', knots(speed, p)
    end do
end program HW10

The issue comes when I get to the function "knots" at the bottom. This is what the function looks like:

real function knots (x, n)
integer, intent(in) :: n
real, dimension(n), intent(inout) :: x
integer :: i

do i = 1, n
    x(i) = (x(i) * 0.514444 )
end do 
return x
end function knots

The code will read in the data fine, as I have the code displaying it properly. However when I want to see the changed data within the wind speed array, all of the data points are zeroes. I'm new to Fortran so I'm not quite sure what to do. Thanks in advance!

d_1999
  • 854
  • 6
  • 16
cdmck004
  • 43
  • 1
  • 7
  • You define your function as a (scalar) `REAL`, but are trying to assign an array to it, I imagine this would be an issue... Also, in your `WRITE` statement, you are writing out the function's return value, not the `speed` array directly. – SteveES Apr 12 '17 at 16:44
  • The function `knots` looks like a complicated way of multiplying a rank-1 array by `0.514444`. The expression `speed*0.514444` (in the main program in this example) would achieve the same end. – High Performance Mark Apr 12 '17 at 17:44

2 Answers2

5

If I try to compile the code given in the question (put all in the same file) with gfortran 4.8.3 I get the following two errors:

First error:

return x
      1
Error: Alternate RETURN statement at (1) is only allowed within a SUBROUTINE

Second error:

        write (*,*) 'Wind Speeds: ', knots(speed, p)
                                           1
Warning: Type mismatch in argument 'x' at (1); passed INTEGER(4) to REAL(4)

Let's deal with the first of these - unlike many other programming languages it is not used to set the return value.

So, why did the compiler just complain that you've done this in a function rather than a subroutine instead of the compiler complaining that you've put a value here? There's a historic feature known as alternate return that is a bit like using a goto -- these are only allowed in subroutines.

So let's replace return x with just return -- this avoids the compiler error, but how does the code know what value to return? When defining functions in fortran you can explicitly specify a name for the result, but if you don't do this then it assumes your result is a variable with the same name as the function, so in your case knots. So in your code the variable to be returned is called knots but it never gets set to anything. By "coincidence" it looks like the bit of memory being used to store the result, which is never explicitly set to anything, is either being initialised to zero by the compiler or you're just accessing uninitialised memory that happens to be full of zeros.

So how do we fix this? Let's define the result explicitly

function knots (x, n) result(y)
  implicit none
  integer, intent(in) :: n
  real, dimension(n), intent(inout) :: x
  real, dimension(n) :: y
  integer :: i

  do i = 1, n
     y(i) = (x(i) * 0.514444 )
  end do
  return
end function knots

If we try to compile we now get a new error!

write (*,*) 'Wind Speeds: ', knots(speed, p)
                                    1
Error: The reference to function 'knots' at (1) either needs an explicit INTERFACE or the rank is incorrect

Functions/subroutines with arguments/return values typically need to have an interface defined . There are many ways to achieve this, I'm going to do it by putting the function in a module:

module myknots
  implicit none
  public :: knots
contains
  function knots (x, n) result(y)
    implicit none
    integer, intent(in) :: n
    real, dimension(n), intent(inout) :: x
    real, dimension(n) :: y
    integer :: i

    do i = 1, n
       y(i) = (x(i) * 0.514444 )
    end do
    return
  end function knots
end module myknots

We then need to add use myknots, only: knots to the top of the main program. This now leaves us with just the second error.

What this is telling us is that you've passed an integer array to a function that expects a real value. This is because speed is declared as integer but x in knots is declared as real. To fix this let us create a new knots function in which x is declared as integer. I'll also use an explicit interface to allow us to refer to either version of knots using the name knots. Doing this the myknots module looks like

module myknots
  implicit none
  private
  public :: knots

  interface knots
     module procedure knots_r
     module procedure knots_i
  end interface knots
contains
  function knots_r (x, n) result(y)
    implicit none
    integer, intent(in) :: n
    real, dimension(n), intent(inout) :: x
    real, dimension(n) :: y
    integer :: i

    do i = 1, n
       y(i) = (x(i) * 0.514444 )
    end do
    return
  end function knots_r

  function knots_i (x, n) result(y)
    implicit none
    integer, intent(in) :: n
    integer, dimension(n), intent(inout) :: x
    real, dimension(n) :: y
    integer :: i

    do i = 1, n
       y(i) = (x(i) * 0.514444 )
    end do
    return
  end function knots_i
end module myknots

The main program looks like

Program HW10
  use myknots, only: knots
  implicit none
  integer :: i
  integer, parameter :: &
       p=38      !lines to read                                                                                                                                                                                    
  real, parameter :: &
       g=9.81    !Value of gravity acceleration                                                                                                                                                                    
  integer , dimension(p) :: direction, speed, rh, speedconv
  real, dimension (p) :: pressure, height, temp, dewpt, mixr
  real :: average

  open(1,file='HW10input.txt', status='old', action='read')

10 format (F6.1, T9, F6.1, T16, F5.1, T23, F5.1, T33, I2, T38, F4.2, T46, I3, T53, I3)

  do i=1,p
     read(1,10)pressure(i), height(i), temp(i), dewpt(i), rh(i), mixr(i), direction(i), speed(i)
  end do


  close (1)

  open(2, file='outputfilehw10.txt', status='new', action='write')
  do i=1,p
     write (*, 20) pressure(i), height(i), temp(i), dewpt(i), rh(i), mixr(i), direction(i), speed(i)
20   format (F6.1, T9, F6.1, T16, F5.1, T23, F5.1, T33, I2, T38, F4.2, T46, I3, T53, I3)
  end do
  write (*,*) 'Average= ', average(temp, p)
  do i=1,p
     write (*,*) 'Wind Speeds: ', knots(speed, p)
  end do
end program HW10

This has fixed all the immediate issues, but you still won't be able to produce an executable as you haven't yet defined the average function. Hopefully the steps above should be enough to allow you to implement this yourself.

Graham
  • 7,431
  • 18
  • 59
  • 84
d_1999
  • 854
  • 6
  • 16
  • Ok this makes a lot of sense now. Follow-up question: would I need to put the module in a separate file and compile that before I compile the main function, or can it go in with the main function? – cdmck004 Apr 12 '17 at 17:05
  • You don't have to put the module in a separate file, but as your project expands I find it's often a good idea to have one module per file. – d_1999 Apr 13 '17 at 07:48
2

You have a function knots which has a real result. Look at the line

write (*,*) 'Wind Speeds: ', knots(speed, p)

The function reference knots(speed, p) is evaluated to return that real result, and that result is then printed.

There are two problems, based on the same misunderstanding: in the function knots the result of the function has the name knots. This isn't the same thing as the intent(inout) dummy argument x.

Problem 1: you don't defined the value of knots in the function.

Problem 2: the value of knots isn't the value you want. You want the value (on return) of speed.

You could either define the result knots to be the output array, or you can use a subroutine instead. I won't go into the details of those two approaches (as there are plenty of resources available there), but I will clearly note: if knots becomes a function with result array there will need to be an explicit interface available when it is referenced.

francescalus
  • 30,576
  • 16
  • 61
  • 96
  • Yes, I went into the details and it became a little long! – d_1999 Apr 12 '17 at 16:55
  • Well I'm not supposed to use subroutines (otherwise this wouldn't be as difficult haha), so I'd have to make knots to be the output array. – cdmck004 Apr 12 '17 at 17:07
  • @cdmck004, I'm glad this answer was useful, but you'll certainly need to take things from the other one. In particular, I missed the `return x` problem (and there's no point repeating it in my answer). You're free to accept whichever answer you like (or none), but I'd suggest mine isn't the most helpful. – francescalus Apr 12 '17 at 17:12
  • No both definitely of your answers were helpful. I really appreciate the help as I have no clue what I'm doing haha – cdmck004 Apr 12 '17 at 17:19