3

The following small code doesn't work:

program main  
  implicit none
  integer, parameter :: dp=selected_real_kind(15)
  real(dp) :: v(9)  
  real(dp) :: vector_norm  
  v(:) = 3.14_dp
  print *, 'Vector norm = ', vector_norm(9,v)  
end program main  
      
 
function vector_norm(n,vec) result(norm)    
  integer, intent(in) :: n  
  real(dp), intent(in) :: vec(n)
  real(dp) :: norm
    
  norm = sqrt(sum(vec**2))  
end function vector_norm

in the main program, I am creating a kind variable dp such that everything will be double precision and I want function vector_norm to yield a double precison result too. I know I can simply use double precision function instead of function statement. Can any of you help how to solve that error while keep using selected_real_kind statement.

Note: error is "A kind type parameter must be a compile-time constant."

  • 1
    Welcome. You should use `implicit none` everywhere and you would find your problem (`dp` not defined in the function). – albert Nov 23 '21 at 13:56
  • Thank you @albert, repeating `integer, parameter :: dp=selected_real_kind(15)` in function solved the problem that I was getting. – Bayraktaroglu Nov 23 '21 at 14:11
  • 4
    Modules are the best way to solve this - in modern code you shouldn't be writing external subprograms. – Ian Bush Nov 23 '21 at 14:14
  • Put the `end program` after the function definition and place a `contains` keyword before the function. This way the main program knows the arguments and types used by the function. – JAlex Nov 23 '21 at 14:44
  • 3
    One solution would be to throw away the function and simply use the intrinsic `norm2`. – High Performance Mark Nov 23 '21 at 15:16
  • Thanks @HighPerformanceMark of course `norm2` is handy to use. I just gave an example function, the point here is the structure of the code (which is wrong and good suggestions have been provided in answers below) – Bayraktaroglu Nov 23 '21 at 15:21

3 Answers3

3

It is convenient to have a separate module defining precision, and then to import this module throughout the code to ensure that precision is used consistently. With a module structure, your code would become

module precision_module
  implicit none
  integer, parameter :: dp=selected_real_kind(15)
end module

module vector_norm_module
  use precision_module, only : dp
  implicit none
contains
  function vector_norm(n,vec) result(norm)    
    integer, intent(in) :: n  
    real(dp), intent(in) :: vec(n)
    real(dp) :: norm
    
    norm = sqrt(sum(vec**2))  
  end function vector_norm
end module

program main
  use precision_module, only : dp
  use vector_norm_module, only : vector_norm 
  implicit none
  real(dp) :: v(9)
  v(:) = 3.14_dp
  print *, 'Vector norm = ', vector_norm(9,v)  
end program main        
veryreverie
  • 2,871
  • 2
  • 13
  • 26
  • No need for the complexity of a separate module, unless the code is going to re-used over and over again. See my answer on how to do this without the defining a separate module. – JAlex Nov 23 '21 at 15:03
  • 2
    @JAlex In general, modules are better and the "complexity" will pay-off well when extending the code. Especially here when showing the technique to a learning user, the modules are no unnecessary complication. On of the problems that overuse of internal subprograms often causes is unintended use of host-associated variables (like forgetting to declare a loop index `i` in the subroutine). Fortran 2018 reacted by `IMPORT, NONE`. – Vladimir F Героям слава Nov 23 '21 at 15:18
  • 1
    @JAlex I disagree. Using modules is a necessity for projects larger than one file. I think that having one way for doing most projects and a different way for small scripts is more unnecessarily complex than just using modules for everything. – veryreverie Nov 23 '21 at 15:53
  • @VladimirF very good point, as access to the host parameters **and variables** can lead to subtle bugs. Global variables are evil and so on. – JAlex Nov 23 '21 at 16:50
1

This works as intended. The trick is the place the function definition inside the program code block by adding the contains keyword. This way the function code block has access to the variables and parameters of the main program.

Code

program FortanConsole1
implicit none

integer, parameter :: dp=selected_real_kind(15)
real(dp) :: v(9)
v(:) = 3.14_dp
print *, 'Vector norm = ', vector_norm(9,v)

contains

function vector_norm(n,vec) result(norm)
integer, intent(in) :: n
real(dp), intent(in) :: vec(n)
real(dp) :: norm

norm = sqrt(sum(vec**2))
end function vector_norm

end program

No need to define vector_norm as an external or the type in the main program if it is defined after the contains keyword. Also no need to define a module for the same effect. Also no need for implicit none in the function as it falls under the scope of the program which already has this declaration.

BTW, Any function after contains can access the variables/parameters of the program to avoid duplication (dp in this case).


Update 1

Removed the array size from vector_norm and used an assumed shape array (any length). This simplifies the API to the function.

program FortanConsole1
...
print *, 'Vector norm = ', vector_norm(v)

contains

function vector_norm(vec) result(norm)
real(dp), intent(in) :: vec(:)
...
end function vector_norm
end program FortanConsole1
JAlex
  • 1,486
  • 8
  • 19
1

The things which define the type parameters and array bounds of objects are known as specification expressions. In the code of the question we see two such specification expressions with real(dp) :: v(9): the kind type parameter dp and the array bound 9.

For a specification expression, only its value matters. Although we don't recommend such things, the primacy of value is why real(kind=33) can be used consistently throughout a program. So, if you want to declare two objects to be the same kind, you just have to use expressions which evaluate the same:

real(kind=33) x1, f1
real(kind=selected_real_kind(15)) x2, f2

x1=f1()
x2=f2()

end

function f1()
  real(kind=33) f1
  f1=1
end function f1

function f2()
  real(kind=selected_real_kind(15)) f2
  f2=2
end function

Above are shown two styles of specification expressions: using a literal constant (33) and appropriately referencing an intrinsic function (selected_real_kind(15)). But specification expressions can also use objects accessible in the scope. We have several techniques for "sharing" objects between scopes, and we can use these techniques in our specification expressions.

Indeed, the declarations in

  function vector_norm(n,vec) result(norm)    
  integer, intent(in) :: n  
  real(dp), intent(in) :: vec(n)

show one such way with name association! The dummy argument n is used in the specification of vec's array bound. This n is argument associated with the actual argument 9 of the main program: we've used a value in the main program to tell the function something about one of its arguments.

Alas, argument association isn't a form of name association which is useful for kind type parameters, because kind type parameters must be constant (which dummy arguments aren't). We can use a dummy argument to tell us the length of a character object or size of an array, but not the character's kind.

We can use other forms of name association to share constants between one scope (such as main program) and another (a function).

We can use host association or use association. I won't recreate code examples from each of those answers, but hopefully this answer motivates why those two approaches work. Neither approach is necessarily better than the other, but once you understand what they aim to do with regards to scope you can compare suitability on other aspects.

Note that the function vector_norm of the first linked answer is itself using both use and host association for the kind parameter (and argument association for the array bound).

francescalus
  • 30,576
  • 16
  • 61
  • 96