Bayraktaroglu
Bayraktaroglu

Reputation: 53

Using consistent precision in main program and function

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."

Upvotes: 3

Views: 188

Answers (3)

jalex
jalex

Reputation: 1524

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

Upvotes: 1

francescalus
francescalus

Reputation: 32366

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).

Upvotes: 1

veryreverie
veryreverie

Reputation: 2981

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        

Upvotes: 3

Related Questions