Trouble using function name as argument in Fortran - fortran

I wanted to make it easier to change a certain function which will be used by a subroutine in a fortran project. However I can not get it to work. I have seen quite a few examples which use the external, however I am not sure if I have to use it, since I put all my function and subroutines in modules.
Here is a simplified example of the problem I am dealing with:
I have the program in a separate file:
program test
use Parameters
use circArrayConstructer
use velocity
use RungeKutta4
implicit none
integer(is) :: N, P, nsteps, i, j
real(fd) :: D, dt
real(fd), allocatable :: coor(:,:)
integer(is), allocatable :: topo(:,:)
integer(is) :: error
read (*,*) D, nsteps, N, P
dt = 1.0 / nsteps
call circArray ( 0.5_fd, 0.5_fd, 0.2_fd, 0.2_fd, N, coor, topo, error )
do i = 1, P
do j = 1, nsteps
if ( mod(P,2) > 0 ) then
call RK4 ( dt, coor, D, vel1, coor )
else
call RK4 ( dt, coor, D, vel2, coor )
end if
end do
end do
end program test
I put each subroutine and all the functions in a separate module and each module has its own file:
The module Parameters just defines constants and variable types:
module Parameters
implicit none
integer, parameter :: fs = selected_real_kind(6)
integer, parameter :: fd = selected_real_kind(15)
integer, parameter :: is = selected_int_kind(9)
integer, parameter :: id = selected_int_kind(18)
real(fd), parameter :: PI = 3.141592653589793
end module Parameters
Module circArrayConstructer contains the subroutine circArray which has the output error, coor and topo, the last two have dimensions N by 2 and since N in an input they have to be allocated.
Module RungeKutta4 contains the subroutine RK4 which is an implementation of the 4th order Runge Kutta method:
module RungeKutta4
use Parameters
use velocity
implicit none
contains
subroutine RK4 ( dt, coorOld, D, vel, coorNew )
implicit none
real(fd), intent(in ) :: dt
real(fd), intent(in ) :: D
real(fd), intent(in ) :: coorOld(:,:)
real(fd), intent(out) :: coorNew(:,:)
real(fd), dimension(size(coorOld,1), size(coorOld,2)) :: k1, k2, k3, k4
real(fd), external :: vel
k1 = vel ( coorOld , D )
k2 = vel ( coorOld + 0.5 * dt * k1, D )
k3 = vel ( coorOld + 0.5 * dt * k2, D )
k4 = vel ( coorOld + dt * k3, D )
coorNew = coorOld + dt / 6.0 * (k1 + 2 * (k2 + k3) + k4)
end subroutine RK4
end module RungeKutta4
And module velocity contains multiple functions:
module velocity
use Parameters
implicit none
contains
function vel1 ( coor, D )
implicit none
real(fd), intent(in) :: D
real(fd), intent(in) :: coor(:,:)
real(fd), dimension(size(coor,1), size(coor,2)) :: vel1
vel1(:,1) = -2.0 * D * coor(:,2) * sin(PI * coor(:,1)) * cos(PI * coor(:,2) ** 2)
vel1(:,2) = D * cos(PI * coor(:,1)) * sin(PI * coor(:,2) ** 2)
end function vel1
function vel2 ( coor, D )
implicit none
real(fd), intent(in) :: D
real(fd), intent(in) :: coor(:,:)
real(fd), dimension(size(coor,1), size(coor,2)) :: vel2
vel2(:,1) = 2.0 * D * (1 - coor(:,2)) * sin(PI * coor(:,1)) * cos(PI * (1 - coor(:,2)) ** 2)
vel2(:,2) = D * cos(PI * coor(:,1)) * sin(PI * (1 - coor(:,2)) ** 2)
end function vel2
end module velocity
Currently when I try to compile the program I get the error: Interface mismatch in dummy procedure 'vel': Type/rank mismatch in function result.
I have tried all kinds of things, such as changing the declaration of vel in RungeKutta4 to real(fd), external :: vel(:,:) but that gives conflicting attributes. But I have run out of ideas on how to make the code work.

With the function in a module, you don't want to use external. The problem is that RK4 doesn't know the properties of the function that it is receiving. You can declare the function with an interface block:
interface
function vel ( corr, D )
import fd
real (fd), intent (in) :: D
real (fd), intent (in) :: corr (:,:)
real(fd), dimension(size(corr,1), size(corr,2)) :: vel
end function vel
end interface

Related

Differential Equation in Fortran

Today I'm trying to evaluate this differential equation for internal energy in a gas in Fortran 90:
du / dt = dT / dt = - λ / ρ
Where u is the internal energy and λ is the cooling function (and they are both functions of temperature T only). ρ is the mass density and we can assume it's constant.
I'm using a Runge-Kutta 2nd order method (heun), and I'm sure I wrote the actual solving algorithm correctly, but I'm pretty sure I'm messing up the implementation. I'm also not sure how to efficiently choose an arbitrary energy scale.
I'm implementing the Right Hand Side with this subroutine:
MODULE RHS
! right hand side
IMPLICIT NONE
CONTAINS
SUBROUTINE dydx(neq, y, f)
INTEGER, INTENT(IN) :: neq
REAL*8, DIMENSION(neq), INTENT(IN) :: y
REAL*8, DIMENSION(neq), INTENT(OUT) :: f
f(1) = -y(1)
END SUBROUTINE dydx
END MODULE RHS
And this is the Heun algorithm I'm using:
SUBROUTINE heun(neq, h, yold, ynew)
INTEGER, INTENT(IN) :: neq
REAL*8, INTENT(IN) :: h
REAL*8, DIMENSION(neq), INTENT(IN) ::yold
REAL*8, DIMENSION(neq), INTENT(OUT) :: ynew
REAL*8, DIMENSION(neq) :: f, ftilde
INTEGER :: i
CALL dydx(neq, yold, f)
DO i=1, neq
ynew(i) = yold(i) + h*f(i)
END DO
CALL dydx(neq, ynew, ftilde)
DO i=1, neq
ynew(i) = yold(i) + 0.5d0*h*(f(i) + ftilde(i))
END DO
END SUBROUTINE heun
Considering both lambda and rho are n-dimensional arrays, i'm saving the results in an array called u_tilde, selecting a starting condition at T = 1,000,000 K
h = 1.d0/n
u_tilde(1) = lambda(n)/density(n) ! lambda(3) is at about T=one million
DO i = 2, n
CALL heun(1, h*i, u_tilde(i-1), u_tilde(i))
ENDDO
This gives me this weird plot for temperature over time.
I would like to have a starting temperature of one million kelvin, and then have it cool down to 10.000 K and see how long it takes. How do I implement these boundary conditions?
What am I doing wrong in RHS and in setting up the calculation loop in the program?
Your implementation of dydx only assigns the first element.
Also, there is no need to define loops for each step, as Fortran90 can do vector operations.
For a modular design, I suggest implementing a custom type that holds your model data, like the mass density and the cooling coefficient.
Here is an example simple implementation, that only holds one scalar value, such that y' = -c y
module mod_diffeq
use, intrinsic :: iso_fortran_env, wp => real64
implicit none
type :: model
real(wp) :: coefficient
end type
contains
pure function dxdy(arg, x, y) result(yp)
type(model), intent(in) :: arg
real(wp), intent(in) :: x, y(:)
real(wp) :: yp(size(y))
yp = -arg%coefficient*y
end function
pure function heun(arg, x0, y0, h) result(y)
type(model), intent(in) :: arg
real(wp), intent(in) :: x0, y0(:), h
real(wp) :: y(size(y0)), k0(size(y0)), k1(size(y0))
k0 = dxdy(arg, x0, y0)
k1 = dxdy(arg, x0+h, y0 + h*k0)
y = y0 + h*(k0+k1)/2
end function
end module
and the above module is used for some cooling simulations with
program FortranCoolingConsole1
use mod_diffeq
implicit none
integer, parameter :: neq = 100
integer, parameter :: nsteps = 256
! Variables
type(model):: gas
real(wp) :: x, y(neq), x_end, h
integer :: i
! Body of Console1
gas%coefficient = 1.0_wp
x = 0.0_wp
x_end = 10.0_wp
do i=1, neq
if(i==1) then
y(i) = 1000.0_wp
else
y(i) = 0.0_wp
end if
end do
print '(1x," ",a22," ",a22)', 'x', 'y(1)'
print '(1x," ",g22.15," ",g22.15)', x, y(1)
! Initial Conditions
h = (x_end - x)/nsteps
! Simulation
do while(x<x_end)
x = x + h
y = heun(gas, x, y, h)
print '(1x," ",g22.15," ",g22.15)', x, y(1)
end do
end program
Note that I am only tracking the 1st element of neq components of y.
The sample output shows exponential decay starting from 1000
x y(1)
0.00000000000000 1000.00000000000
0.390625000000000E-01 961.700439453125
0.781250000000000E-01 924.867735244334
0.117187500000000 889.445707420492
0.156250000000000 855.380327695983
0.195312500000000 822.619637044785
0.234375000000000 791.113666448740
0.273437500000000 760.814360681126
0.312500000000000 731.675505009287
0.351562500000000 703.652654704519
0.390625000000000 676.703067251694
0.429687500000000 650.785637155231
0.468750000000000 625.860833241968
0.507812500000000 601.890638365300
0.546875000000000 578.838491418631
0.585937500000000 556.669231569681
...
Also, if you wanted the above to implement runge-kutta 4th order you can include the following in the mod_diffeq module
pure function rk4(arg, x0, y0, h) result(y)
type(model), intent(in) :: arg
real(wp), intent(in) :: x0, y0(:), h
real(wp) :: y(size(y0)), k0(size(y0)), k1(size(y0)), k2(size(y0)), k3(size(y0))
k0 = dxdy(arg, x0, y0)
k1 = dxdy(arg, x0+h/2, y0 + (h/2)*k0)
k2 = dxdy(arg, x0+h/2, y0 + (h/2)*k1)
k3 = dxdy(arg, x0+h, y0 + h*k2)
y = y0 + (h/6)*(k0+2*k1+2*k2+k3)
end function

Compile Fortran code with #:if defined('FOO')

A Fortran code has two definitions of a subroutine within an if defined block, as shown below. If I manually remove of the definitions, the code can be compiled, but that's not what the author intended. Compiling with gfortran -c -cpp does not work. What is the right way to compile it?
#:if defined('SLICOT')
subroutine dlyap(TT, RQR, P0, ns, info)
! Computes the solution to the discrete Lyapunov equation,
! P0 = TT*P0*TT' + RQR
! where (inputs) TT, RQR and (output) P0 are ns x ns (real) matrices.
!--------------------------------------------------------------------------------
integer, intent(in) :: ns
real(wp), intent(in) :: TT(ns,ns), RQR(ns,ns)
integer, intent(out) :: info
real(wp), intent(out) :: P0(ns,ns)
! for slicot
real(wp) :: scale, U(ns,ns), UH(ns, ns), rcond, ferr, wr(ns), wi(ns), dwork(14*ns*ns*ns), sepd
integer :: iwork(ns*ns), ldwork
integer :: t
UH = TT
P0 = -1.0_wp*RQR
!call sb03md('D','X', 'N', 'T', ns, UH, ns, U, ns, P0, ns, &
! scale, sepd, ferr, wr, wi, iwork, dwork, 14*ns*ns*ns, info)
!if (ferr > 0.000001_wp) call dlyap_symm(TT, RQR, P0, ns, info)
if (info .ne. 0) then
print*,'SB03MD failed. (info = ', info, ')'
P0 = 0.0_wp
info = 1
do t = 1,ns
P0(t,t)=1.0_wp
end do
return
else
! P0 = 0.5_wp*P0 + 0.5_wp*transpose(P0)
info = 0
end if
end subroutine dlyap
#:else
! from elmar
SUBROUTINE DLYAP(A, QQ, Sigma, nx, status)
! doubling, calling DSYMM and DGEMM
! Sigma = A * Sigma * A' + B * B'
! output Sigma is symmetric
IMPLICIT NONE
integer, intent(in) :: nx
integer, intent(out) :: status
real(wp), intent(in) :: QQ(nx,nx), A(nx,nx)
real(wp), intent(out) :: Sigma(nx,nx)
INTEGER, PARAMETER :: maxiter = 100
DOUBLE PRECISION, PARAMETER :: tol = 1.0d-8
INTEGER :: iter, i
LOGICAL :: converged
DOUBLE PRECISION, DIMENSION(Nx,Nx) :: AA, AAA, AASigma, Sigma0
Sigma0 = QQ
! Sigma0 = B B'
! Sigma0 = 0.0d0
! call DSYRK('U','N',Nx,Nw,1.0d0,B,Nx,0.0d0,Sigma0,Nx)
! ! fill up lower triangular -- necessary for DGEMM below
! FORALL (i=2:Nx) Sigma0(i,1:i-1) = Sigma0(1:i-1,i)
converged = .false.
iter = 0
AA = A
DO
iter = iter + 1
! call sandwichplus(Sigma, AA, Nx, Sigma0, Nx)
! MANUAL SANDWICHPLUS: Sigma = AA * Sigma0 * AA' + Sigma
call DSYMM('R','U',Nx,Nx,1.0d0,Sigma0,Nx,AA,Nx,0.0d0,AASigma,Nx)
Sigma = Sigma0 ! this line requires Sigma0 to
call DGEMM('N','T',Nx,Nx,Nx,1.0d0,AASigma,Nx,AA,Nx,1.0d0,Sigma,Nx)
! balance for symmetry
Sigma = 0.5d0 * (Sigma + transpose(Sigma))
IF (abs(maxval(Sigma - Sigma0)) < tol) converged = .true.
! print *, iter, abs(maxval(Sigma - Sigma0)), tol
! Sigma = (Sigma + transpose(Sigma)) / dble(2)
IF (converged .OR. (iter > maxiter)) EXIT
! AAA = AA * AA
call DGEMM('N','N',Nx,Nx,Nx,1.0d0,AA,Nx,AA,Nx,0.0d0,AAA,Nx)
AA = AAA
Sigma0 = Sigma
END DO
IF (converged) THEN
status = 0
ELSE
status = -1
END IF
END SUBROUTINE DLYAP
#:endif

I have a piece of code written in fortran 90 output is different when I compile it different times

I use the same compiler (gfortran) and computer every time I compile it and I don't change the source code, but on different compilations it gives a completely different outcome. Sometimes a reasonable outcome and sometimes a wrong outcome.
For instance I compiled it and got the next very unreasonable outputs.
0.0000000000000000 , 3.0902604013843218E+049 , 3.0902604013843218E+049 , 3.0902604013843218E+049 , 5.3524880238158376E+049
2.0000000000000000 , -6.1610238730665058E+049 , -6.1610238730665058E+049 , -6.1610238730665058E+049 , 1.0671206374795975E+050
4.0000000000000000 , 5.5751160679618236E+049 , 5.5751160679618236E+049 , 5.5751160679618236E+049 , 9.6563842878035016E+049
6.0000000000000000 , -1.0179425493222214E+049 , -1.0179425493222214E+049 , -1.0179425493222214E+049 , 1.7631282146122754E+049
8.0000000000000000 , 2.4002992709421553E+049 , 2.4002992709421553E+049 , 2.4002992709421553E+049 , 4.1574402906423475E+049
10.000000000000000 , 3.5499818567499908E+049 , 3.5499818567499908E+049 , 3.5499818567499908E+049 , 6.1487489418386840E+049
12.000000000000000 , -3.5465339877133604E+049 , -3.5465339877133604E+049 , -3.5465339877133604E+049 , 6.1427770574893967E+049
14.000000000000000 , 3.7523505062483277E+049 , 3.7523505062483277E+049 , 3.7523505062483277E+049 , 6.4992617246289011E+049
Then without changing anything I recompiled the same code and ran it again and got the more reasonable first result:
0.0000000000000000 , -0.20075827532679802 , 5.7540609466025759E-003 , 0.33972754855979093 , 0.39465402770022856
Why does this happen? Is it a problem in my code or the compiler. I leave the code just in case its useful to answer the question. I'm just starting to learn fortran, and sorry for the comments in spanish.
EDIT: In this program I use fgsl, a fortran interface to the gnu scientific library, it can be found here: https://github.com/reinh-bader/fgsl
program trace_of_product
use fgsl
implicit none
real(fgsl_double) :: p1, p2, p3, t, r, omega, tmax, rad, zav, yav, xav, integ1, integ2, integ3
integer(fgsl_int) :: i, j, k, nin, nt, jmax
complex(fgsl_double) :: wigner_func, z_sym, y_sym, x_sym
real(fgsl_double), parameter :: pi = 3.14159265359
character(len=3) :: filenumber
omega = 0.01
nin = 500
nt = 50
jmax = 9
tmax = 100
r = sqrt(1.0d0*jmax)
write(filenumber, '(I0.3)') jmax
open(unit = 1, file = 'data'//trim(filenumber)//'.csv')
do k = 0,nt
t = k*tmax/nt
zav = 0.0d0
yav = 0.0d0
xav = 0.0d0
do j = 1,jmax,2
integ1 = 0.0d0
integ2 = 0.0d0
integ3 = 0.0d0
!
! Esta parte del código calcula la integral del producto de la
! j-función de Wigner y el j-simbolo de los operadores de posición en la región
! [0,2pi)x[0,pi)x[0,4pi) y dV = sin(theta) d(phi) d(theta) d(psi)
!
do i = 0,nin
p1 = 2*pi*rand()
p2 = pi*rand()
p3 = 4*pi*rand()
integ1 = integ1 + realpart(wigner_func(j,r,t,omega,p1,p2,p3))*realpart(z_sym(j,t,omega,p1,p2,p3))&
*sin(p2)
integ2 = integ2 + realpart(wigner_func(j,r,t,omega,p1,p2,p3))*realpart(y_sym(j,t,omega,p1,p2,p3))&
*sin(p2)
integ3 = integ3 + realpart(wigner_func(j,r,t,omega,p1,p2,p3))*realpart(x_sym(j,t,omega,p1,p2,p3))&
*sin(p2)
end do
integ1 = integ1*pi*(j+1)/nin
integ2 = integ2*pi*(j+1)/nin
integ3 = integ3*pi*(j+1)/nin
zav = zav +integ1
yav = yav +integ2
xav = xav +integ3
end do
rad = sqrt(xav**2+yav**2+zav**2)
write(1,*) t,',',xav,',',yav,',',zav,',',rad
write(*,*) t,',',xav,',',yav,',',zav,',',rad
end do
end program
!
! Esta función calcula la j-función de Wigner para un estado coherente de dos
! modos |ab>=|a>|b>, rho = |ab><ab|. N y J están restingidos a que N+J=j
!
function wigner_func(jc,r,t,omega,phi,theta,psi) result(Wp)
use fgsl
implicit none
real(fgsl_double), intent(in) :: r, t, omega, phi, theta, psi
integer(fgsl_int), intent(in) :: jc
complex(fgsl_double) :: wigner_D, Wp
real(fgsl_double) :: wigner_small_d, cg
integer(fgsl_int) :: m, mp, k, l, q, N, J
real(fgsl_double), parameter :: pi = 3.14159265359
do N = 0,jc
J = jc-N
do k = abs(N-J),jc
do m = -J,J
do mp = -N,N
do l = -k,k
do q = -k,k
Wp = Wp + exp(-r**2) * r**(2*jc) * (2*k+1) * sin(pi/8)**(jc-m-mp) * cos(pi/8)**(jc+m+mp)* &
cg(N,mp,J,m,k,l)*wigner_D(k,l,q,phi+omega*t,theta,psi)*wigner_small_d(k,q,N-J,pi/2) / &
sqrt(Gamma(N-mp+1.0d0)*Gamma(J-m+1.0d0)*Gamma(N+mp+1.0d0)*Gamma(J+m+1.0d0)*(jc+1)*(2*N+1))
end do
end do
end do
end do
end do
end do
end function wigner_func
!
! Esta función calcula el j-simbolo del operador z, j = 2l + 1
!
function z_sym(j,t,omega,phi,theta,psi) result(Wz)
use fgsl
implicit none
real(fgsl_double), intent(in) :: t, omega, phi, theta, psi
integer(fgsl_int), intent(in) :: j
complex(fgsl_double) :: Wigner_D, Wz
real(fgsl_double) :: wigner_small_d, cg
integer(fgsl_int) :: l,m,k,qp,q
real(fgsl_double), parameter :: pi = 3.14159265359
if ( mod(j,2) == 1 ) then
l = (j-1)/2
do m = -l,l
do k = 1,j
do qp = -k,k
do q = -k,k
Wz = Wz + sqrt(((l+1.0d0)**2-m**2)/((4*(l+1)**2-1)*(j+1)))*(2*k+1)*&
Wigner_D(k,qp,q,phi+omega*t,theta,psi)* &
(cg(l+1,m,l,m,k,qp)*wigner_small_d(k,q,1,pi/2)/sqrt(j+1.0d0)+&
cg(l,m,l+1,m,k,qp)*wigner_small_d(k,q,-1,pi/2)/sqrt(1.0d0*j))
end do
end do
end do
end do
end if
end function z_sym
!
! Esta función calcula el j-simbolo del operador y, j = 2l + 1
!
function y_sym(j,t,omega,phi,theta,psi) result(Wy)
use fgsl
implicit none
real(fgsl_double), intent(in) :: t, omega, phi, theta, psi
integer(fgsl_int), intent(in) :: j
complex(fgsl_double) :: Wigner_D, Wy
real(fgsl_double) :: wigner_small_d, cg
integer(fgsl_int) :: l, m, k, s, q
real(fgsl_double), parameter :: pi = 3.141596265359
if ( mod(j,2) == 1 ) then
l = (j-1)/2
do m = -l,l
do k = 1,j
do q = -k,k
do s = -k,k
Wy = Wy+cmplx(0,0.5)*sqrt((2*k+1)/((4*(l+1)**2-1)*(j+1.0d0)))*wigner_D(k,s,q,phi+omega*t,theta,psi)*&
(sqrt((l+m+1)*(l+m+2.0d0))*(wigner_small_d(k,q,-1,pi/2)*cg(l,m,l+1,m+1,k,s)/sqrt(1.0d0*j)- &
wigner_small_d(k,q,1,pi/2)*cg(l+1,m+1,l,m,k,s)/sqrt(j+2.0d0)) + sqrt((l-m)*(l-m+1.0d0))*(&
wigner_small_d(k,q,-1,pi/2)*cg(l,m+1,l+1,m,k,s)/sqrt(1.0d0*j)-wigner_small_d(k,q,1,pi/2)*&
cg(l+1,m,l,m+1,k,s)/sqrt(j+2.0d0)))
end do
end do
end do
end do
end if
end function
!
! Esta función calcula el j-simbolo del operador x, j = 2l + 1
!
function x_sym(j,t,omega,phi,theta,psi) result(Wx)
use fgsl
implicit none
real(fgsl_double), intent(in) :: t, omega, phi, theta, psi
integer(fgsl_int), intent(in) :: j
complex(fgsl_double) :: Wigner_D, Wx
real(fgsl_double) :: wigner_small_d, cg
integer(fgsl_int) :: l, m, k, s, q
real(fgsl_double), parameter :: pi = 3.141596265359
if ( mod(j,2) == 1 ) then
l = (j-1)/2
do m = -l,l
do k = 1,j
do q = -k,k
do s = -k,k
Wx = Wx+0.5*sqrt((2*k+1)/((4*(l+1)**2-1)*(j+1.0d0)))*wigner_D(k,s,q,phi+omega*t,theta,psi)*&
(-sqrt((l+m+1)*(l+m+2.0d0))*(wigner_small_d(k,q,-1,pi/2)*cg(l,m,l+1,m+1,k,s)/sqrt(1.0d0*j)+ &
wigner_small_d(k,q,1,pi/2)*cg(l+1,m+1,l,m,k,s)/sqrt(j+2.0d0)) + sqrt((l-m)*(l-m+1.0d0))*(&
wigner_small_d(k,q,-1,pi/2)*cg(l,m+1,l+1,m,k,s)/sqrt(1.0d0*j)+wigner_small_d(k,q,1,pi/2)*&
cg(l+1,m,l,m+1,k,s)/sqrt(j+2.0d0)))
end do
end do
end do
end do
end if
end function
!
! Esta función calcula los coeficientes de Clebsch-Gordan
!
function cg(j,m,j1,m1,j2,m2) result(cgn)
use fgsl
implicit none
integer(fgsl_int), intent(in) :: j, m, j1, m1, j2, m2
real(fgsl_double) :: cgn
cgn = (-1)**(-j1+j2-m)*sqrt(2.0*j+1)*fgsl_sf_coupling_3j(2*j1,2*j2,2*j,2*m1,2*m2,-2*m)
end function cg
!
! Esta función es la d-función de Wigner
!
function wigner_small_d(j,m,mp,theta) result(d)
use fgsl
implicit none
integer(fgsl_int), intent(in) :: j, m, mp
integer(fgsl_int) :: k
real(fgsl_double), intent(in) :: theta
real(fgsl_double) :: d
do k = max(0,m-mp),min(j-mp,j+m)
d = d + (-1)**k * cos(theta/2)**(2*j+m-mp-2*k) * sin(theta/2)**(mp-m+2*k) / &
(Gamma(j-mp-k+1.0d0)*Gamma(k+1.0d0)*Gamma(mp-m+k+1.0d0)*Gamma(j+m-k+1.0d0))
end do
d = sqrt(Gamma(j+m+1.0d0)*Gamma(j-m+1.0d0)*Gamma(j+mp+1.0d0)*Gamma(j-mp+1.0d0))*d
end function wigner_small_d
!
! Esta función es la D-función de Wigner
!
function wigner_D(j,m,mp,phi,theta,psi) result(D)
use fgsl
implicit none
integer(fgsl_int), intent(in) :: j, m, mp
real(fgsl_double), intent(in) :: phi, theta, psi
real(fgsl_double) :: wigner_small_d
complex(fgsl_double) :: D
D = exp(-cmplx(0,1)*phi*m)*wigner_small_d(j,m,mp,theta)*exp(-cmplx(0,1)*psi*mp)
end function wigner_D
Does it also happen when you run the program multiple times without recompiling it? If that is the case, you are probably reading a variable that has not been assigned yet.
When debugging a piece of fortran code it is useful to include some debug flags. I use:
gfortran myprog.f90 -g -O0 -Wall -fcheck=all -fbacktrace
You can look them up in the gnu fortran compiler documentation if your interested in what they are doing.

Solving a linear system with the Gauss method returns wrong results

Thank you guys, all your advices are relevant, but I gave up of Fortran and translated my code to Python. There, with just 1 or 2 little bugs my code worked perfect
I wrote a program to solve a linear system using the Gauss method. I wrote all the algorithms, the forward elimination and the back substitution and I made a lot of others subroutines and I don't know anymore what's wrong, I don't if is something wrong with my code or if some problem programming in Fortran because I'm new in this language. I'll put my code below and the linear system that I should find a solution
PROGRAM metodo_Gauss
IMPLICIT NONE
REAL :: det_a_piv
INTEGER :: n, i, j
REAL, DIMENSION(:,:), ALLOCATABLE :: a, a_piv
INTEGER, DIMENSION(:), ALLOCATABLE :: p
REAL, DIMENSION(:), ALLOCATABLE :: b, x
PRINT*, "Entre com a dimensão n do sistema a ser resolvido"
READ*, n
! allocate memory
ALLOCATE(a(n, n))
ALLOCATE(a_piv(n, n))
ALLOCATE(p(n))
ALLOCATE(b(n))
ALLOCATE(x(n))
CALL matriz_a(n, a)
CALL vetor_b(n, b)
a_piv(1:n, 1:n) = a(1:n, 1:n)
DO i = 1, n
x(i) = 0
END DO
CALL eliminacao(n, a, a_piv, p)
det_a_piv = (-1) ** n
DO j = 1, n
det_a_piv = det_a_piv * a_piv(j, j)
END DO
IF (det_a_piv == 0) THEN
PRINT*, "O sistema linear é indeterminado"
ELSE IF (abs(det_a_piv) <= 1) THEN
PRINT*, "O sistema linear é mal-condicionado"
ELSE
CALL substituicao(n, a_piv, p, b, x)
PRINT*, "A solução do sistema é:"
PRINT*, x
END IF
END PROGRAM metodo_Gauss
SUBROUTINE matriz_a(n, a)
IMPLICIT NONE
INTEGER, INTENT(in) :: n
REAL, DIMENSION(n,n), INTENT(inout) :: a
INTEGER :: i, j !Indícios usados em loops para percorrer os arrays
PRINT*, "Por favor digite os valores do elementos da matriz sistema linear seguindo pela ordem das linhas até o final:"
DO i = 1, n
DO j = 1, n
READ*, a(i,j)
END DO
END DO
END SUBROUTINE matriz_a
SUBROUTINE vetor_b(n, b)
IMPLICIT NONE
INTEGER, INTENT(in) :: n
REAL, DIMENSION(n), INTENT(inout) :: b
INTEGER :: i
PRINT*, "Por favor entre com os elementos do vetor b:"
DO i = 1, n
READ*, b(i)
END DO
END SUBROUTINE vetor_b
SUBROUTINE eliminacao(n, a, a_piv, p)
IMPLICIT NONE
INTEGER, INTENT(in) :: n
REAL, DIMENSION(n, n), INTENT(in) :: a
REAL, DIMENSION(n, n), INTENT(out) :: a_piv
INTEGER, DIMENSION(n), INTENT(out) :: p
INTEGER :: i, j, local, dim
REAL :: mult
DO i = 1, (n - 1)
dim = n - 1
CALL local_pivo(dim, a(i:n, i), local)
a_piv(i, i:n) = a(local, i:n)
a_piv(local, i:n) = a(i, i:n)
p(i) = local
DO j = (i + 1), n
mult = (-1) * (a_piv(j,i) / a_piv(local,i))
a_piv(j,i) = mult
a_piv(j, j:n) = a_piv(j, j:n) + mult * a_piv(i, j:n)
END DO
END DO
END SUBROUTINE eliminacao
SUBROUTINE local_pivo(n, a, local)
IMPLICIT NONE
INTEGER, INTENT(in) :: n
REAL, DIMENSION(n), INTENT(in) :: a
INTEGER, INTENT(inout) :: local
INTEGER :: i
local = 1
DO i = 2, n
IF ((ABS(a(i))) > ABS(a(local))) THEN
local = i
END IF
END DO
END SUBROUTINE local_pivo
SUBROUTINE substituicao(n, a_piv, p, b, x)
IMPLICIT NONE
INTEGER, INTENT(in) :: n
REAL, DIMENSION(n, n), INTENT(in) :: a_piv
REAL, DIMENSION(n), INTENT(out) :: b, x
INTEGER, DIMENSION(n), INTENT(in) :: p
INTEGER :: i, j, k, l, pivo
REAL :: aux
DO i = 1, (n - 1)
pivo = p(i)
IF (pivo /= i) THEN
aux = b(i)
b(i) = b(pivo)
b(pivo) = aux
END IF
DO j = (i + 1), n
b(j) = a_piv(j, i) * b(j) + b(i)
END DO
END DO
DO k = n, 1, -1
IF (k == n) THEN
x(n) = b(n) / a_piv(n, n)
ELSE
x(k) = (b(k) + a_piv(k, n) * x(n)) / a_piv(k, k)
DO l = n, k, -1
x(l) = x(l) + (a_piv(k, l) * x(l)) / a_piv(k, k)
END DO
END IF
END DO
END SUBROUTINE substituicao
Here it is the system that I'm trying to solve
My input is:
4
4
3
2
2
2
1
1
2
2
2
2
4
6
1
1
2
5
8
3
1
My output is:
-40.5000000 -40.2500000 -3.75000000 -37.5000000
But the output should be:
6.500000
-44.000000
72.000000
-16.500000

Can't replace Fortran real variables by double precision variables or more precision

I am using a known code (CAMB) which generates values like this :
k(h/Mpc) Pk/s8^2(Mpc/h)^3
5.2781500000e-06 1.9477400000e+01
5.5479700000e-06 2.0432300000e+01
5.8315700000e-06 2.1434000000e+01
6.1296700000e-06 2.2484700000e+01
6.4430100000e-06 2.3587000000e+01
6.7723700000e-06 2.4743400000e+01
7.1185600000e-06 2.5956400000e+01
7.4824500000e-06 2.7228900000e+01
7.8649500000e-06 2.8563800000e+01
8.2669900000e-06 2.9964100000e+01
I would like to get more precision on the generated values, like this :
k(h/Mpc) Pk/s8^2(Mpc/h)^3
5.3594794736e-06 1.8529569626e+01
5.6332442000e-06 1.9437295914e+01
5.9209928622e-06 2.0389484405e+01
6.2234403231e-06 2.1388326645e+01
6.5413364609e-06 2.2436098099e+01
6.8754711720e-06 2.3535198212e+01
7.2266739153e-06 2.4688137054e+01
7.5958159869e-06 2.5897554398e+01
7.9838137026e-06 2.7166225433e+01
8.3916311269e-06 2.8497039795e+01
8.8202796178e-06 2.9893053055e+01
9.2708232842e-06 3.1357446670e+01
9.7443817140e-06 3.2893573761e+01
Here the section of code that produces the data :
I tried to do the following modifications in the declarations of variables at the beginning of code above :
1)First try :
!Export files of total matter power spectra in h^{-1} Mpc units, against k/h.
Type(MatterTransferData), intent(in) :: MTrans
Type(CAMBdata) :: State
character(LEN=Ini_max_string_len), intent(IN) :: FileNames(*)
character(LEN=name_tag_len) :: columns(3)
integer itf, i, unit
integer points
! Added : way of declaring double precision
integer, parameter :: wp = selected_real_kind(15,307)
real(wp), dimension(:,:), allocatable :: outpower
but it doesn't compile :
real(wp), dimension(:,:), allocatable :: outpower
1
Error: Symbol ‘wp’ at (1) has no IMPLICIT type
../results.f90:3660:25:
allocate(outpower(points,ncol))
1
Error: Allocate-object at (1) is neither a data pointer nor an allocatable variable
../results.f90:3676:16:
outpower(:,1) = exp(PK_data%matpower(:,1))
1
Error: Unclassifiable statement at (1)
../results.f90:3679:20:
outpower(:,3) = exp(PK_data%vvpower(:,1))
1
Error: Unclassifiable statement at (1)
compilation terminated due to -fmax-errors=4.
make[1]: *** [results.o] Error 1
make: *** [camb] Error 2
2) Also, I tried :
!Export files of total matter power spectra in h^{-1} Mpc units, against k/h.
Type(MatterTransferData), intent(in) :: MTrans
Type(CAMBdata) :: State
character(LEN=Ini_max_string_len), intent(IN) :: FileNames(*)
character(LEN=name_tag_len) :: columns(3)
integer itf, i, unit
integer points
! Added : way of declaring double precision
double precision, dimension(:,:), allocatable :: outpower
but same thing, no compilation succeeded
call Transfer_GetMatterPowerS(State, MTrans, outpower(1,1), itf, minkh,dlnkh, points)
1
Error: Type mismatch in argument ‘outpower’ at (1); passed REAL(8) to REAL(4)
make[1]: *** [results.o] Error 1
make: *** [camb] Error 2
UPDATE 1:
with -fmax-errors=1, I get the following :
call Transfer_GetMatterPowerS(State, MTrans, outpower(1,1), itf, minkh,dlnkh, points)
1
Error: Type mismatch in argument ‘outpower’ at (1); passed REAL(8) to REAL(4)
compilation terminated due to -fmax-errors=1.
Except the solution given by #Steve with compilation option -freal-4-real-8, isn't really there another solution that I could include directly into code, i.e the section that I have given ?
UPDATE 2: here below the 3 relevant subroutines Transfer_GetMatterPowerS , Transfer_GetMatterPowerData and Transfer_SaveMatterPower that produces the error when trying to get double precision :
subroutine Transfer_GetMatterPowerS(State, MTrans, outpower, itf, minkh, dlnkh, npoints, var1, var2)
class(CAMBdata) :: state
Type(MatterTransferData), intent(in) :: MTrans
integer, intent(in) :: itf, npoints
integer, intent(in), optional :: var1, var2
real, intent(out) :: outpower(*)
real, intent(in) :: minkh, dlnkh
real(dl) :: outpowerd(npoints)
real(dl):: minkhd, dlnkhd
minkhd = minkh; dlnkhd = dlnkh
call Transfer_GetMatterPowerD(State, MTrans, outpowerd, itf, minkhd, dlnkhd, npoints,var1, var2)
outpower(1:npoints) = outpowerd(1:npoints)
end subroutine Transfer_GetMatterPowerS
subroutine Transfer_GetMatterPowerData(State, MTrans, PK_data, itf_only, var1, var2)
!Does *NOT* include non-linear corrections
!Get total matter power spectrum in units of (h Mpc^{-1})^3 ready for interpolation.
!Here there definition is < Delta^2(x) > = 1/(2 pi)^3 int d^3k P_k(k)
!We are assuming that Cls are generated so any baryonic wiggles are well sampled and that matter power
!spectrum is generated to beyond the CMB k_max
class(CAMBdata) :: State
Type(MatterTransferData), intent(in) :: MTrans
Type(MatterPowerData) :: PK_data
integer, intent(in), optional :: itf_only
integer, intent(in), optional :: var1, var2
double precision :: h, kh, k, power
integer :: ik, nz, itf, itf_start, itf_end, s1, s2
s1 = PresentDefault (transfer_power_var, var1)
s2 = PresentDefault (transfer_power_var, var2)
if (present(itf_only)) then
itf_start=itf_only
itf_end = itf_only
nz = 1
else
itf_start=1
nz= size(MTrans%TransferData,3)
itf_end = nz
end if
PK_data%num_k = MTrans%num_q_trans
PK_Data%num_z = nz
allocate(PK_data%matpower(PK_data%num_k,nz))
allocate(PK_data%ddmat(PK_data%num_k,nz))
allocate(PK_data%nonlin_ratio(PK_data%num_k,nz))
allocate(PK_data%log_kh(PK_data%num_k))
allocate(PK_data%redshifts(nz))
PK_data%redshifts = State%Transfer_Redshifts(itf_start:itf_end)
h = State%CP%H0/100
do ik=1,MTrans%num_q_trans
kh = MTrans%TransferData(Transfer_kh,ik,1)
k = kh*h
PK_data%log_kh(ik) = log(kh)
power = State%CP%InitPower%ScalarPower(k)
if (global_error_flag/=0) then
call MatterPowerdata_Free(PK_data)
return
end if
do itf = 1, nz
PK_data%matpower(ik,itf) = &
log(MTrans%TransferData(s1,ik,itf_start+itf-1)*&
MTrans%TransferData(s2,ik,itf_start+itf-1)*k &
*const_pi*const_twopi*h**3*power)
end do
end do
call MatterPowerdata_getsplines(PK_data)
end subroutine Transfer_GetMatterPowerData
subroutine Transfer_SaveMatterPower(MTrans, State,FileNames, all21cm)
use constants
!Export files of total matter power spectra in h^{-1} Mpc units, against k/h.
Type(MatterTransferData), intent(in) :: MTrans
Type(CAMBdata) :: State
character(LEN=Ini_max_string_len), intent(IN) :: FileNames(*)
character(LEN=name_tag_len) :: columns(3)
integer itf, i, unit
integer points
! Added : way of declaring double precision
!integer, parameter :: wp = selected_real_kind(15,307)
!real(wp), dimension(:,:), allocatable :: outpower
double precision, dimension(:,:), allocatable :: outpower
real minkh,dlnkh
Type(MatterPowerData) :: PK_data
integer ncol
logical, intent(in), optional :: all21cm
logical all21
!JD 08/13 Changes in here to PK arrays and variables
integer itf_PK
all21 = DefaultFalse(all21cm)
if (all21) then
ncol = 3
else
ncol = 1
end if
do itf=1, State%CP%Transfer%PK_num_redshifts
if (FileNames(itf) /= '') then
if (.not. transfer_interp_matterpower ) then
itf_PK = State%PK_redshifts_index(itf)
points = MTrans%num_q_trans
allocate(outpower(points,ncol))
!Sources
if (all21) then
call Transfer_Get21cmPowerData(MTrans, State, PK_data, itf_PK)
else
call Transfer_GetMatterPowerData(State, MTrans, PK_data, itf_PK)
!JD 08/13 for nonlinear lensing of CMB + LSS compatibility
!Changed (CP%NonLinear/=NonLinear_None) to CP%NonLinear/=NonLinear_none .and. CP%NonLinear/=NonLinear_Lens)
if(State%CP%NonLinear/=NonLinear_none .and. State%CP%NonLinear/=NonLinear_Lens) then
call State%CP%NonLinearModel%GetNonLinRatios(State, PK_data)
PK_data%matpower = PK_data%matpower + 2*log(PK_data%nonlin_ratio)
call MatterPowerdata_getsplines(PK_data)
end if
end if
outpower(:,1) = exp(PK_data%matpower(:,1))
!Sources
if (all21) then
outpower(:,3) = exp(PK_data%vvpower(:,1))
outpower(:,2) = exp(PK_data%vdpower(:,1))
outpower(:,1) = outpower(:,1)/1d10*const_pi*const_twopi/MTrans%TransferData(Transfer_kh,:,1)**3
outpower(:,2) = outpower(:,2)/1d10*const_pi*const_twopi/MTrans%TransferData(Transfer_kh,:,1)**3
outpower(:,3) = outpower(:,3)/1d10*const_pi*const_twopi/MTrans%TransferData(Transfer_kh,:,1)**3
end if
call MatterPowerdata_Free(PK_Data)
columns = ['P ', 'P_vd','P_vv']
unit = open_file_header(FileNames(itf), 'k/h', columns(:ncol), 15)
do i=1,points
write (unit, '(*(E15.6))') MTrans%TransferData(Transfer_kh,i,1),outpower(i,:)
end do
close(unit)
else
if (all21) stop 'Transfer_SaveMatterPower: if output all assume not interpolated'
minkh = 1e-4
dlnkh = 0.02
points = log(MTrans%TransferData(Transfer_kh,MTrans%num_q_trans,itf)/minkh)/dlnkh+1
! dlnkh = log(MTrans%TransferData(Transfer_kh,MTrans%num_q_trans,itf)/minkh)/(points-0.999)
allocate(outpower(points,1))
call Transfer_GetMatterPowerS(State, MTrans, outpower(1,1), itf, minkh,dlnkh, points)
columns(1) = 'P'
unit = open_file_header(FileNames(itf), 'k/h', columns(:1), 15)
do i=1,points
write (unit, '(*(E15.6))') minkh*exp((i-1)*dlnkh),outpower(i,1)
end do
close(unit)
end if
deallocate(outpower)
end if
end do
end subroutine Transfer_SaveMatterPower