Use Fortran-code in C - c

I try to use a fortran-routine in C, but I doesn't work. I don't know where I made a mistake. Here my Fortran-code including the Integration-Module, which I want to use in C:
module integration
implicit none
contains
function Integrate(func, a,b, intsteps) result(integral)
interface
real function func(x)
real, intent(in) :: x
end function func
end interface
real :: integral, a, b
integer :: intsteps
intent(in) :: a, b, intsteps
optional :: intsteps
real :: x, dx
integer :: i,n
integer, parameter :: rk = kind(x)
n = 1000
if (present(intsteps)) n = intsteps
dx = (b-a)/n
integral = 0.0_rk
do i = 1,n
x = a + (1.0_rk * i - 0.5_rk) * dx
integral = integral + func(x)
end do
integral = integral * dx
end function
end module integration
real(c_float) function wrapper_integrate(func,a,b, intsteps) result(integral) bind(C, name='integrate')
use iso_c_binding
use integration
interface
real(c_float) function func(x) bind(C)
use, intrinsic :: iso_c_binding
real(c_float), intent(in) :: x
end function func
end interface
real(c_float) :: a,b
integer(c_int),intent(in) :: intsteps
optional :: intsteps
if (present(intsteps)) then
integral = Integrate(func,a,b,intsteps)
else
integral = Integrate(func,a,b)
endif
end function wrapper_integrate
and my C-Code:
#include <stdio.h>
#include <math.h>
float sin2(float x) {
return sin(x) * sin(x);
}
float integrate(float(*func)(float), float a, float b, int intsteps);
int main() {
float integral;
integral = integrate(sin2,0.,1.,10000);
printf("%f",integral);
return 0;
}
if I execute
g++ -c main.c
gfortran -c integration.f95
g++ main.o integration.o
I get
undefined reference to `integrate(float (*)(float), float, float, int)'
Does anyone know how to handle this?

If you are using the module ISO_C_Binding, you can directly passing a function from C to Fortran as a function pointer C_FUNPTR.
See here for details.
In your case, this would look like:
real(c_float) function wrapper_integrate(func, a, b, intsteps) result(integral) bind(C, name='integrate')
use iso_c_binding
use integration
abstract interface
function iFunc(x) bind(C)
use, intrinsic :: iso_c_binding
real(c_float) :: iFunc
real(c_float), intent(in) :: x
end function iFunc
end interface
type(C_FUNPTR), INTENT(IN), VALUE :: func
real(c_float) :: a,b
integer(c_int),intent(in) :: intsteps
optional :: intsteps
procedure(iFunc),pointer :: myfunc
call c_f_procpointer(func, myfunc)
if (present(intsteps)) then
integral = Integrate(myfunc,a,b,intsteps)
else
integral = Integrate(myfunc,a,b)
endif
end function wrapper_integrate
Obviously, your solution is more elegant ;-)
Also, please note that Fortran passes variables by reference (unless you specify the VALUE attribute, which you don't). Therefore, you need to change you C code accordingly:
#include <stdio.h>
#include <math.h>
float sin2(float *x) {
return sin(*x) * sin(*x);
}
float integrate(float(*func)(float*), float* a, float* b, int* intsteps);
int main() {
float integral;
float a=0.;
float b=1.;
int intsteps=10000;
integral = integrate(sin2, &a, &b, &intsteps);
printf("%f",integral);
return 0;
}

You are using the C++ compiler, not the C one. Linking conventions may be different.
And you forgot to link with the math library (because of sin)
gcc -c main.c
gfortran -c integration.f95
gcc main.o integration.o -lm

The OP used the gnu c++ compiler. Here's a solution, using information from several respondants, that worked, using the c++ compiler -- not the c compiler -- for me:
file 'integration.f95':
module integration
implicit none
contains
function Integrate(func, a, b, intsteps) result(integral)
interface
real function func(x)
real, intent(in) :: x
end function func
end interface
real :: integral, a, b
integer :: intsteps
intent(in) :: a, b, intsteps
optional :: intsteps
real :: x, dx
integer :: i,n
integer, parameter :: rk = kind(x)
n = 1000
if (present(intsteps)) n = intsteps
dx = (b-a)/n
integral = 0.0_rk
do i = 1,n
x = a + (1.0_rk * i - 0.5_rk) * dx
integral = integral + func(x)
end do
integral = integral * dx
end function
end module integration
real(c_float) function wrapper_integrate(func, a, b, intsteps) result(integral) bind(C, name='integrate')
use iso_c_binding
use integration
abstract interface
function iFunc(x) bind(C)
use, intrinsic :: iso_c_binding
real(c_float) :: iFunc
real(c_float), intent(in) :: x
end function iFunc
end interface
type(C_FUNPTR), INTENT(IN), VALUE :: func
real(c_float) :: a,b
integer(c_int),intent(in) :: intsteps
optional :: intsteps
procedure(iFunc),pointer :: myfunc
call c_f_procpointer(func, myfunc)
if (present(intsteps)) then
integral = Integrate(myfunc,a,b,intsteps)
else
integral = Integrate(myfunc,a,b)
endif
end function wrapper_integrate
file 'main.c':
#include <stdio.h>
#define _USE_MATH_DEFINES
#include <math.h>
float sin2(float *x) {
return sin(*x) * sin(*x);
}
float integrate(float(*func)(float*), float* a, float* b, int* intsteps);
int main() {
int intsteps=10000;
float integral;
float a=0.;
float b=3.1416;
integral = integrate(sin2, &a, &b, &intsteps);
printf("The numerical value of \\int_0^\\pi dx sin^2x = %f\n",integral);
printf("The exact value of \\int_0^\\pi dx sin^2x = %f\n",M_PI_2);
return 0;
}
file 'compile.txt':
gcc -c main.c
gfortran -c integration.f95
g++ -o intsinq main.o integration.o

Related

Sum and assign of array is slower in derived types

I was comparing the performance of doing a sum followed by an assignment of two arrays, in the form of c=a+b, between a native Fortran type, real, and a derived data type that only contains one array of real. The class is very simple: it contains operators for addition and assignment and a destructor, as follows:
module type_mod
use iso_fortran_env
type :: class_t
real(8), dimension(:,:), allocatable :: a
contains
procedure :: assign_type
generic, public :: assignment(=) => assign_type
procedure :: sum_type
generic :: operator(+) => sum_type
final :: destroy
end type class_t
contains
subroutine assign_type(lhs, rhs)
class(class_t), intent(inout) :: lhs
type(class_t), intent(in) :: rhs
lhs % a = rhs % a
end subroutine assign_type
subroutine destroy(this)
type(class_t), intent(inout) :: this
if (allocated(this % a)) deallocate(this % a)
end subroutine destroy
function sum_type (lhs, rhs) result(res)
class(class_t), intent(in) :: lhs
type(class_t), intent(in) :: rhs
type(class_t) :: res
res % a = lhs % a + rhs % a
end function sum_type
end module type_mod
The assign subroutine contains different modes of operations, just for the sake of benchmarking.
To test it against performing the same operations on a real I created the following module
module subroutine_mod
use type_mod, only: class_t
contains
subroutine sum_real(a, b, c)
real(8), dimension(:,:), intent(inout) :: a, b, c
c = a + b
end subroutine sum_real
subroutine sum_type(a, b, c)
type(class_t), intent(inout) :: a, b, c
c = a + b
end subroutine sum_type
end module subroutine_mod
Everything is executed in the program below, considering arrays of size (10000,10000) and repeating the operation 100 times:
program test
use subroutine_mod
integer :: i
integer :: N = 100 ! Number of times to repeat the assign
integer :: M = 10000 ! Size of the arrays
real(8) :: tf, ts
real(8), dimension(:,:), allocatable :: a, b, c
type(class_t) :: a2, b2, c2
allocate(a2%a(M,M), b2%a(M,M), c2%a(M,M))
a2%a = 1.0d0
b2%a = 2.0d0
c2%a = 3.0d0
allocate(a(M,M), b(M,M), c(M,M))
a = 1.0d0
b = 2.0d0
c = 3.0d0
! Benchmark timing with
call cpu_time(ts)
do i = 1, N
call sum_type(a2, b2, c2)
end do
call cpu_time(tf)
write(*,*) "Type : ", tf-ts
call cpu_time(ts)
do i = 1, N
call sum_real(a, b, c)
end do
call cpu_time(tf)
write(*,*) "Real : ", tf-ts
end program test
To my surprise, the operation with my derived datatype consistently underperformed the operation with the Fortran arrays by a factor of 2 with gfortran and a factor of 10 with ifort. For instance, using the CHECK_SIZE mode, which saves allocation time, I got the following timings compiling with the -O2 flag:
gfortran
Data type: 33 s
Real : 13 s
ifort
Data type: 30 s
Real : 3 s
Question
Is this normal behaviour? If so, are there any recommendations to achieve better performance?
Context
To provide some context, the type with a single array will be very useful for a code refactoring task, where we need to keep similar interfaces to a previous type.
Compiler versions
gfortran 9.4.0
ifort 2021.6.0 20220226
You are worried about allocation time, but you do a lot of allocations of arrays of shape [M,M] for the derived type, and almost none for the intrinsic type.
The only allocations for the intrinsic type are in the main program, for a, b and c. These are outside the timing loop.
For the derived type, you allocate for a2%a, b2%a and c2%a (again outside the timing loop), but also res%a in the function sum, N times inside the timing loop.
Equally, inside the sum_real subroutine the assignment statement c=a+b involves no allocatable object but inside sum_type the c in c=a+b is an allocatable array: the compiler checks whether c is allocated and if so, whether its shape matches the right-hand side expression.
In summary: you are not comparing like with like. There's a lot of overhead in wrapping an intrinsic array as an allocatable component of a derived type.
Tangential to your timing concerns is the "cleverness" of the subroutine assign. It's horrible.
Calling an argument lhs when it's associated with the right-hand side of the assignment statement is a little confusing, but the select case construct is confusing beyond a little.
In
case (ASSUMED_SIZE)
this % a = lhs % a
under rules where the rest of the program makes any sense, invokes a couple of checks:
is this%a allocated? If not, allocate it to the shape of lhs%a.
if it is allocated, check whether the shape matches lhs%a, if not deallocate it then allocate it to the shape of lhs%a.
Those checks and actions which are done manually in the CHECK_SIZE case, in other words.
The final subroutine does nothing of value, so the entire assign subroutine's execution can be replaced by this%a = lhs%a.
(Things would be different if the final subroutine had substantive effect or the compiler had been asked to ignore the rules of intrinsic assignment using -fno-realloc-arrays or -nostandard-realloc-lhs for example, or this%a(:,:)=lhs%a had been used.)

How to split a single Fortran file into separate subroutine and function files

Another week, another silly question from someone trying not to smash their head against their desk. I have a Fortran90 file that does what I want it to do, no errors, and correct output. Now I need to separate this thing that works into its constituents, i.e. two subroutine files, two function files and a driver program. How do I do this without breaking it, because it is broken...The main issue is passing arrays...I think.
Working single file code:
program testnew
implicit none
integer,parameter :: p14r300 = SELECTED_REAL_KIND(14,300)
integer,parameter :: k7 = SELECTED_INT_KIND(7)
integer(kind=k7) :: n, ng
real(kind=p14r300), dimension(:), allocatable :: xarr
real(kind=p14r300), dimension(:), allocatable :: xabsc
real(kind=p14r300), dimension(:), allocatable :: weight
real(kind=p14r300) :: tol
do n=2,4
allocate (xarr(n))
allocate (xabsc(n))
allocate (weight(n))
call gauss_leg_int(ng, xabsc, weight)
print *, ng, xabsc, weight
deallocate (xarr)
deallocate (xabsc)
deallocate (weight)
enddo
return
contains
subroutine gauss_leg_int(ng, xabsc, weight)
!==================================================================
! Subroutine that organizes the computations to find the abscissas
! and weights for Gauss-Legendre integration, where ng is the
! number of integration points(integer, input), and xabsc and
! weight are real arrays of length ng (output) that hold the
! abscissas and weights, respectively.
!==================================================================
integer(kind=k7) :: ng, i, iter
real(kind=p14r300) :: x, w
real(kind=p14r300), dimension(:), allocatable :: weight, xabsc
do i=1,n
call leg_root(n, tol, xarr)
xabsc=xarr
ng=n
!do iter=1,n
x=xabsc(i)
print *,x
w=2/((1-x**2)*leg_deriv(n, x)**2)
!enddo
weight(i)=w
enddo
end subroutine gauss_leg_int
subroutine leg_root(n, tol, xarr)
!==================================================================
! Subroutine that finds the set of roots of a Legendre polynomial,
! where n is the degree of the polynomial (input,integer), and tol
! is an absolute tolerance(input,real) for stopping the iteration
! when abs(P_l(x_i))<=tol.
!==================================================================
real(kind=p14r300) :: a, pi, x, y, pl, tol ! Declare real variables
real(kind=p14r300), dimension(:), allocatable :: xarr ! Array
integer(kind=k7) :: i, n, iter ! Declare integer variables
a=1.0 ! Value to use on the next line
pi=4*atan(a) ! Calculate Pi
tol=1.d-14
do i=1,n
x=-cos(pi*(i-0.25)/(n+0.5)) ! Initial x value
do iter=1,20 ! Set maximum number of iterations
y=x-leg_poly(n, x)/leg_deriv(n, x)
pl=leg_poly(n, y)-leg_poly(n, x)
x=y ! Once value of y is correct, make x the same
if (abs(pl)<=tol) exit ! Once tolerance is reached, exit
enddo
!write (*,*) x
xarr(i)=x
!print *,xarr
enddo
!xarr(1,i*4)=x
end subroutine leg_root
function leg_poly(n, x) result(pn)
!==================================================================
! Function for evaluating a given Legendre polynomial using the
! recurrence relation, where n is the degree of the
! polynomial(input, integer), and x is the location(input, real)
! in the interval -1<=x<=1 in which to evaluate the polynomial.
! The function result is the real value of P_n(x).
!==================================================================
real(kind=p14r300) :: pn, x, pln(0:n)
integer(kind=k7) :: l, n
pln(0)=1.0 ! First Legendre polynomial
pln(1)=x ! Second Legendre polynomial
if (n<=1) then ! Set the first two polynomials
pn=pln(n)
else ! Starts the recurrence to generate
do l=1,n-1 ! higher degree polynomials
pln(l+1)=((2.0*l+1.0)*x*pln(l)-l*pln(l-1))/(l+1)
enddo
pn=pln(n)
endif
end function leg_poly
function leg_deriv(n, x) result(pdn)
!=================================================================
! Function for evaluating the derivatives of a given Legendre
! polynomial using the recurrence relation, where n is the degree
! of the polynomial(input, integer), and x is the
! location(input, real) in the interval -1<=x<=1 in which to
! evaluate the derivative. The function result is the real value
! of Pd_n(x).
!=================================================================
real(kind=p14r300) :: pdn, x, pdln(0:n)
integer(kind=k7) :: l, n
pdln(0)=0 ! Derivative of first Legendre polynomial
pdln(1)=1.0 ! Derivative of second Legendre polynomial
if (n<=1) then ! Set the first two Legendre polynomial
pdn=pdln(n) ! derivatives
else ! Starts the recurrence to generate
do l=1,n-1 ! higher degree polynomial derivatives
pdln(l+1)=((2.0*l+1.0)*x*pdln(l)-(l+1)*pdln(l-1))/l
enddo
pdn=pdln(n)
endif
end function leg_deriv
end program
Here is how solved my conundrum, first I divided it all up, created a module for precision parameters, and edited each file to deal with the fact that arrays had to be passed:
Precision MODULE:
MODULE Precision
!===========================================================
! Module to be used to declare precision parameters for any
! program using it.
!===========================================================
IMPLICIT NONE
INTEGER, PARAMETER :: p14r300=SELECTED_REAL_KIND(14,300)
INTEGER, PARAMETER :: k7=SELECTED_INT_KIND(7)
END MODULE Precision
Driver program:
program testnew
USE Precision
implicit none
integer(kind=k7) :: n, ng
real(kind=p14r300), allocatable :: xabsc(:)
real(kind=p14r300), allocatable :: weight(:)
do n=2,4 ! Set range based on provided table
! Allocate memory to dynamic arrays
allocate (xabsc(n))
allocate (weight(n))
! Call subroutine to obtain values of interest
call gauss_leg_int(n, xabsc, weight)
! Print values to stdout
print *, n
print *, xabsc
print *, weight
! Deallocate memory
deallocate (xabsc)
deallocate (weight)
enddo
end program testnew
Functions:
function leg_poly(n, x) result(pn)
!==================================================================
! Function for evaluating a given Legendre polynomial using the
! recurrence relation, where n is the degree of the
! polynomial(input, integer), and x is the location(input, real)
! in the interval -1<=x<=1 in which to evaluate the polynomial.
! The function result is the real value of P_n(x).
!==================================================================
USE Precision
IMPLICIT NONE
real(kind=p14r300) :: pn, pln(0:n)
real(kind=p14r300), intent(in) :: x
integer(kind=k7), intent(in) :: n
integer(kind=k7) :: l
pln(0)=1.0 ! First Legendre polynomial
pln(1)=x ! Second Legendre polynomial
if (n<=1) then ! Set the first two polynomials
pn=pln(n)
else ! Starts the recurrence to generate
do l=1,n-1 ! higher degree polynomials
pln(l+1)=((2.0*l+1.0)*x*pln(l)-l*pln(l-1))/(l+1)
enddo
pn=pln(n)
endif
end function leg_poly
function leg_deriv(n, x) result(pdn)
!=================================================================
! Function for evaluating the derivatives of a given Legendre
! polynomial using the recurrence relation, where n is the degree
! of the polynomial(input, integer), and x is the
! location(input, real) in the interval -1<=x<=1 in which to
! evaluate the derivative. The function result is the real value
! of Pd_n(x).
!=================================================================
USE Precision
IMPLICIT NONE
REAL(KIND=p14r300) :: pdn, x, pdln(0:n)
INTEGER(KIND=k7), INTENT(IN) :: n
INTEGER(KIND=k7) :: l
pdln(0)=0 ! Derivative of first Legendre polynomial
pdln(1)=1.0 ! Derivative of second Legendre polynomial
if (n<=1) then ! Set the first two Legendre polynomial
pdn=pdln(n) ! derivatives
else ! Starts the recurrence to generate
do l=1,n-1 ! higher degree polynomial derivatives
pdln(l+1)=((2.0*l+1.0)*x*pdln(l)-(l+1)*pdln(l-1))/l
enddo
pdn=pdln(n)
endif
end function leg_deriv
Subroutines:
subroutine leg_root(n, xarr)
!==================================================================
! Subroutine that finds the set of roots of a Legendre polynomial,
! where n is the degree of the polynomial (input,integer), and tol
! is an absolute tolerance(input,real) for stopping the iteration
! when abs(P_l(x_i))<=tol.
!==================================================================
USE Precision
IMPLICIT NONE
real(kind=p14r300) :: a, pi, x, y, pl ! Declare real variables
real(kind=p14r300) :: tol
real(kind=p14r300), intent(out) :: xarr(n)
integer(kind=k7) :: i, iter ! Declare integer variables
integer(kind=k7), intent(in) :: n
real(kind=p14r300),EXTERNAL :: leg_poly, leg_deriv
a=1.0 ! Value to use on the next line
pi=4*atan(a) ! Calculate Pi
tol=1.d-14
do i=1,n
x=-cos(pi*(i-0.25)/(n+0.5)) ! Initial x value
do iter=1,20 ! Set maximum number of iterations
y=x-leg_poly(n, x)/leg_deriv(n, x)
pl=leg_poly(n, y)-leg_poly(n, x)
x=y ! Make x the same as the computed y to repeat
! calculation
if (abs(pl)<=tol) exit ! Once tolerance is reached, exit
enddo
xarr(i)=x ! Place x values into array
enddo
end subroutine leg_root
subroutine gauss_leg_int(ng, xabsc, weight)
!==================================================================
! Subroutine that organizes the computations to find the abscissas
! and weights for Gauss-Legendre integration, where ng is the
! number of integration points(integer, input), and xabsc and
! weight are real arrays of length ng (output) that hold the
! abscissas and weights, respectively.
!==================================================================
USE Precision
IMPLICIT NONE
integer(kind=k7) :: i
integer(kind=k7), intent(in) :: ng
real(kind=p14r300) :: x, w
real(kind=p14r300), EXTERNAL :: leg_deriv
real(kind=p14r300) :: xarr(ng)
real(kind=p14r300), intent(out) :: weight(ng)
real(kind=p14r300), intent(out) :: xabsc(ng)
do i=1,ng
call leg_root(ng, xabsc) ! Call subroutine to use xarr
x=xabsc(i) ! Loop over each x value per ng
w=2/((1-x**2)*leg_deriv(ng, x)**2) ! calculate weight
weight(i)=w ! Place weight values into array
enddo
end subroutine gauss_leg_int

Strange initialization behavior for an array constructor with implied do in gfortran

Let's say I have 3 double-precision arrays,
real*8, dimension(n) :: x, y, z
which are initialized as
x = 1.
y = (/ (1., i=1,n) /)
z = (/ (1. +0*i, i=1,n) /)
They should initialize all elements of all arrays to 1. In ifort (16.0.0 20150815), this works as intended for any n within the range of the declared precision. That is, if we initialize n as
integer*4, parameter :: n
then as long as n < 2147483647, the initialization works as intended for all declarations.
In gfortran (4.8.5 20150623 Red Hat 4.8.5-16), the initialization fails for y (array comprehension with constant argument) as long as n>65535, independent of its precision. AFAIK, 65535 is the maximum of a unsigned short int, aka unsigned int*2 which is well within the range of integer*4.
Below is an MWE:
program test
implicit none
integer*4, parameter :: n = 65536
integer*4, parameter :: m = 65535
real*8, dimension(n) :: x, y, z
real*8, dimension(m) :: a, b, c
integer*4 :: i
print *, huge(n)
x = 1.
y = (/ (1., i=1,n) /)
z = (/ (1.+0*i, i=1,n) /)
print *, x(n), y(n), z(n)
a = 1.
b = (/ (1., i=1,m) /)
c = (/ (1.+0*i, i=1,m) /)
print *, a(m), c(m), c(m)
end program test
Compiling with gfortran (gfortran test.f90 -o gfortran_test), it outputs:
2147483647
1.0000000000000000 0.0000000000000000 1.0000000000000000
1.0000000000000000 1.0000000000000000 1.0000000000000000
Compiling with ifort (ifort test.f90 -o ifort_test), it outputs:
2147483647
1.00000000000000 1.00000000000000 1.00000000000000
1.00000000000000 1.00000000000000 1.00000000000000
What gives?
There is indeed a big difference in how the compiler treats the array constructors. For n<=65535 there is the actual array of [1., 1., 1.,...] stored in the object file (or in some of the intermediate representations).
For a larger array the compiler generates a loop:
(*(real(kind=8)[65536] * restrict) atmp.0.data)[offset.1] = 1.0e+0;
offset.1 = offset.1 + 1;
{
integer(kind=8) S.2;
S.2 = 0;
while (1)
{
if (S.2 > 65535) goto L.1;
y[S.2] = (*(real(kind=8)[65536] * restrict) atmp.0.data)[S.2];
S.2 = S.2 + 1;
}
L.1:;
}
it appears to me, that first it sets only one element of a temporary array and then it copies the (mostly undefined) temporary array to y. And that is wrong. Valgrind also reports usage of uninitialized memory.
For a default real we have
while (1)
{
if (shadow_loopvar.2 > 65536) goto L.1;
(*(real(kind=4)[65536] * restrict) atmp.0.data)[offset.1] = 1.0e+0;
offset.1 = offset.1 + 1;
shadow_loopvar.2 = shadow_loopvar.2 + 1;
}
L.1:;
{
integer(kind=8) S.3;
S.3 = 0;
while (1)
{
if (S.3 > 65535) goto L.2;
y[S.3] = (*(real(kind=4)[65536] * restrict) atmp.0.data)[S.3];
S.3 = S.3 + 1;
}
L.2:;
}
We have two loops now, one sets the whole temporary array and the second one copies that to y and everything is fine.
Conclusion: a compiler bug.
The issue was fixed by GCC developers who read this question. The bug is tracked at https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84931
They also identified that the problem is connected to type conversion. The constructor has default precision 1. and with single precision array there is no type conversion, but for a double precision array there is some type conversion. That caused the difference for these two cases.

Is there a difference between initializing a sequence of parameters and a parameter array?

If I am going to calculate a polynomial by using nested parentheses, is there a difference between declaring each constant individually and declaring them as an array?
For example, is there a difference between
real(kind = c_double), parameter :: &
P0 = .5, &
P1 = .8, &
P2 = -.1, &
P3 = -.7, &
P4 = -.4, &
P5 = -.6, &
P6 = -.2
and calculating
x = ((((((P6 * a + P5) * a + P4) * a + P3) * a + P2) * a + P1) * a + P0)
or
real(kind = c_double), parameter, dimension(7) :: P = &
[.5, .8, -.1, -.7, -.4, -.6, -.2]
and calculating
x = ((((((P(7) * a + P(6)) * a + P(5)) * a + P(4)) * a + P(3)) * a + P(2)) * a + P(1))
Yes there are differences, but these aren't differences that are important in your use here.
The differences come from the fact that given the (slightly different from the question) declarations
real, parameter :: P1=0.1, P2=0.2, P(2)=[0.1,0.2]
P1 and P2 are constants, but P(1) and P(2) aren't. P is a constant, but that's not the same thing. So, P1 may be used in some circumstances where P(1) cannot.
However, in expressions like
((((((P6 * a + P5) * a + P4) * a + P3) * a + P2) * a + P1) * a + P0)
the constant nature is not important and elements of P may be used there just as easily.
Differences arise in cases such as complex literal constants and (for integers) kind parameters in literal constants. Also, with P(1) being an array element it can be used in some ways unsuitable for P1.
I'll note a couple of things specific to the question:
given P0 is used, the array could be indexed from 0: real, parameter, dimension(0:6) :: P=[...];
one advantage of the array is that the number of elements may (in current Fortran, not F90) be implied: real, parameter, dimension(0:*) :: P=[...]
I want to point out that in the section on constant expressions, the standard says
A constant expression is ...
(1) a constant or a subobject of a constant.
So where can P1 be used where P(1) cannot? Certainly P(1) can be used where P1 cannot, however.
The array method is really attractive in my opinion because it's so much easier to understand array expressions -- there could be some irregularity in the nested expression that the reader could miss, but not in the array formula:
program polyval
use ISO_C_BINDING, only:C_DOUBLE
implicit none
integer, parameter :: wp = C_DOUBLE
real(wp), parameter :: P(0:*) = &
[0.5_wp,0.8_wp,-0.1_wp,-0.7_wp,-0.4_wp,-0.6_wp,-0.2_wp]
real(wp) a, x
integer i
a = 10
x = sum([(a**i*P(i),i=0,ubound(P,1))])
write(*,*) x
end program polyval
EDIT: I thought that by now Fortran compilers might be smart enough to recognize the above idiom for polynomial evaluation, but apparently I was wrong. I would have thought that
function poly1(x)
use ISO_FORTRAN_ENV, only: wp=> REAL64
real(wp) x
real(wp) poly1
real(wp), parameter :: P0 = 0.5_wp, P1 = 0.8_wp, P2 = -0.1_wp, &
P3 = -0.7_wp, P4 = -0.4_wp, P5 = -0.6_wp, P6 = -0.2_wp
poly1 = (((((P6*x+P5)*x+P4)*x+P3)*x+P2)*x+P1)*x+P0
end function poly1
and
function poly2(x)
use ISO_FORTRAN_ENV, only: wp=> REAL64
real(wp) x
real(wp) poly2
real(wp), parameter :: P(0:6) = &
[0.5_wp,0.8_wp,-0.1_wp,-0.7_wp,-0.4_wp,-0.6_wp,-0.2_wp]
integer i
poly2 = sum([(x**i*P(i),i=0,ubound(P,1))])
end function poly2
would result in similar code, but both gfortran -S -O3 -ffast-math -funroll-loops poly2.f90 and ifort /Fa /c /fast /Qipo- poly2.f90 compute powers of x and an effective DOT_PRODUCT rather than using an efficient method. So maybe it is required to write out the expression longhand like assembly rather than a high-level language to get reasonable performance in this context.
EDIT: OK, so there seems to be one context where a REAL named constant can be used but a REAL constant expression cannot.
program test2
use ISO_FORTRAN_ENV, only:wp=>REAL64
implicit none
complex(wp) x
real(wp), parameter :: P1 = 4*atan(1.0_wp)
real(wp), parameter :: P(1) = exp(1.0_wp)
x = (P1,0)
write(*,*) x
! x = (P(1),0) ! Fails because literal or named constant is required
write(*,*) x
end program test2
But searching the standard I think that's the only case. Was it in f2003 that a named constant was first permitted in a complex-literal-constant? For completeness I offer an example where the constant expression can work but not the named constant:
module mymod
use ISO_FORTRAN_ENV,only:wp=>REAL64
implicit none
contains
subroutine sub(x)
real(wp) x(*)
write(*,*) x(1)
end subroutine sub
end module mymod
program test3
use mymod
implicit none
real(wp), parameter :: P1 = 4*atan(1.0_wp)
real(wp), parameter :: P(1) = exp(1.0_wp)
! call sub(P1) ! Fails because array actual argument required
call sub(P(1)) ! Succeeds due to sequence association
end program test3

Array assignment erases previous values on array

I'm running the following code, that is the implementation of a Runge-Kutta method to solve a system of differential equations.
The main code just calls the rk subroutine, which is the implementation itself, and myfun is just an example to test the code.
program main
use ivp_odes
implicit none
double precision, allocatable :: t(:), y(:,:)
double precision :: t0, tf, y0(2), h
integer :: i
t0 = 0d0
tf = 0.5d0
y0 = [0d0, 0d0]
h = 0.1d0
call rk4(t, y, myfun, t0, tf, y0, h)
do i=0,size(t)
print *, t(i), y(:,i)
end do
contains
pure function myfun(t,y) result(dy)
! input variables
double precision, intent(in) :: t, y(:)
! output variables
double precision :: dy(size(y))
dy(1) = -4*y(1) + 3*y(2) + 6
dy(2) = -2.4*y(1) + 1.6*y(2) + 3.6
end function myfun
end program main
and the subroutine is inside a module:
module ivp_odes
implicit none
contains
subroutine rk4(t, y, f, t0, tf, y0, h)
! input variables
double precision, intent(in) :: t0, tf, y0(1:)
double precision, intent(in) :: h
interface
pure function f(t,y0) result(dy)
double precision, intent(in) :: t, y0(:)
double precision :: dy(size(y))
end function
end interface
! output variables
double precision, allocatable :: t(:), y(:,:)
! Variáveis auxiliares
integer :: i, m, NN
double precision, allocatable :: k1(:), k2(:), k3(:), k4(:)
m = size(y0)
allocate(k1(m),k2(m),k3(m),k4(m))
NN = ceiling((tf-t0)/h)
if (.not. allocated(y)) then
allocate(y(m,0:NN))
else
deallocate(y)
allocate(y(m,0:NN))
end if
if (.not. allocated(t)) then
allocate(t(0:NN))
else
deallocate(t)
allocate(t(0:NN))
end if
t(0) = t0
y(:,0) = y0
do i=1,NN
k1(:) = h * f(t(i-1) , y(:,i-1) )
k2(:) = h * f(t(i-1)+h/2 , y(:,i-1)+k1(:)/2)
k3(:) = h * f(t(i-1)+h/2 , y(:,i-1)+k2(:)/2)
k4(:) = h * f(t(i-1)+h , y(:,i-1)+k3(:) )
y(:,i) = y(:,i-1) + (k1(:) + 2*k2(:) + 2*k3(:) + k4(:))/6
t(i) = t(i-1) + h
end do
deallocate(k1,k2,k3,k4)
return
end subroutine rk4
end module ivp_odes
The problem here is that the assignment in the rk subroutine
y(:,i) = y(:,i-1) + (k1(:) + 2*k2(:) + 2*k3(:) + k4(:))/6
is erasing the previous values calculated. In the i-th iteration of the do-loop, it erases the previous values of the array y and assigns just the i-th column of the array y, so when the subroutine ends, y has only the last value saved.
Since Fortran has implemented element-wise operations and assignments to arrays, I think the code this is easier to read and probably runs faster than doing assignments to each element in a loop. So, why is it not working? What am I missing in the assignment here? Shouldn't it just change the values in the i-th row, instead of also erasing the rest of the array?
This is a typical case of accessing an array out of its bounds. You can find these errors easily using the appropriate compiler flags. With gfortran, this would be -fbounds-check.
With such checks you will find the error to be an erroneous size of the function result in the interface block - dy should have the same length as y0 (the one-dimensional dummy argument of f), and not y:
interface
pure function f(t,y0) result(dy)
double precision, intent(in) :: t, y0(:)
double precision :: dy(size(y0))
end function
end interface
Additionally, although not related to your particular error, you started indexing of t and the second dimension of y with zero. So you need to adjust the loop in the main program run to size(t)-1 only, or use ubound(t). Otherwise you will, again, exceed the boundaries of the arrays.

Resources