Related
How can overload operator(+) and assigment(-) for my class?
the compiler shows me the following message
Error: Component to the right of a part reference with nonzero rank must not have the ALLOCATABLE
attribute at (1).
For the assigment(=), I have no idea how to do it.
For c ++ it was easier. Return pointner this
function zmat_zmat_add(zmatrix1,zmatrix2) result(res_zmat_zmat)
type(zmatrix_type), dimension(:,:), intent(in) :: zmatrix1
type(zmatrix_type), dimension(:,:), intent(in) :: zmatrix2
type(zmatrix_type) :: res_zmat_zmat
integer :: rows
integer :: i,j ! liczniki pętli
rows=3
do i=1, rows
do j=1, rows
res_zmat_zmat%zmatrix_data(i,j)%realis= &
zmatrix1%zmatrix_data(i,j)%realis + zmatrix2%zmatrix_data(i,j)%realis
res_zmat_zmat%zmatrix_data(i,j)%imaginalis = &
zmatrix1%zmatrix_data(i,j)%imaginalis + &
zmatrix2%zmatrix_data(i,j)%imaginalis
enddo
enddo
end function zmat_zmat_add
rest code
module zmatrix_module
implicit none
type, public :: zcomplex_type
real :: realis
real :: imaginalis
end type zcomplex_type
type, extends(zcomplex_type), public :: zmatrix_type
type(zcomplex_type), dimension(:,:), allocatable, public :: zmatrix_data
end type zmatrix_type
public :: zmatrix_allocate
public :: zmatrix_free
public :: zmatrix_set
public :: zmatrix_print
interface operator(+)
procedure zzadd
procedure zmat_zmat_add
end interface
contains
function zzadd(z1,z2) result(res)
type(zcomplex_type), intent(in) :: z1
type(zcomplex_type), intent(in) :: z2
type(zcomplex_type) :: res
res%realis=z1%realis+z2%realis
res%imaginalis= z1%imaginalis +z2%imaginalis
end function zzadd
function zmat_zmat_add(zmatrix1,zmatrix2) result(res_zmat_zmat)
type(zmatrix_type), dimension(:,:), intent(in) :: zmatrix1
type(zmatrix_type), dimension(:,:), intent(in) :: zmatrix2
type(zmatrix_type) :: res_zmat_zmat
integer :: rows
integer :: i,j
rows=3
do i=1, rows
do j=1, rows
res_zmat_zmat%zmatrix_data(i,j)%realis= &
zmatrix1%zmatrix_data(i,j)%realis + zmatrix2%zmatrix_data(i,j)%realis
res_zmat_zmat%zmatrix_data(i,j)%imaginalis = &
zmatrix1%zmatrix_data(i,j)%imaginalis + &
zmatrix2%zmatrix_data(i,j)%imaginalis
enddo
enddo
end function zmat_zmat_add
subroutine zmatrix_allocate(zarray,rows)
type(zmatrix_type), intent(out) :: zarray
integer, intent(in) :: rows
allocate(zarray%zmatrix_data(1:rows, 1:rows))
end subroutine zmatrix_allocate
subroutine zmatrix_free(zarray)
type(zmatrix_type), intent(inout) :: zarray
deallocate(zarray%zmatrix_data)
end subroutine zmatrix_free
subroutine zmatrix_set(zarray, rows, re_values, im_values)
type(zmatrix_type), intent(inout) :: zarray
integer, intent(in) :: rows
real, intent(in) :: re_values, im_values
integer :: i,j
do i=1, rows
do j=1, rows
zarray%zmatrix_data(i,j)%realis = re_values
zarray%zmatrix_data(i,j)%imaginalis = im_values
enddo
enddo
end subroutine zmatrix_set
subroutine zmatrix_print(array,rows)
type(zmatrix_type), intent(in) :: array
integer, intent(in) :: rows
integer i,j
do i=1, rows
write(*,*) (array%zmatrix_data(i,j), j=1, rows)
enddo
write(*,*)
end subroutine zmatrix_print
end module zmatrix_module
Program main
use zmatrix_module
implicit none
type(zmatrix_type) :: mat1
type(zmatrix_type) :: mat2
type(zmatrix_type) :: mat3
type(zcomplex_type) :: z1
type(zcomplex_type) :: z2
type(zcomplex_type) :: z3
integer :: rows
rows=2
print *, " AAAAAAA"
call zmatrix_allocate(mat1,rows)
call zmatrix_set(mat1,rows,10.0,8.0)
call zmatrix_print(mat1,rows)
print *, "BBBBBBBB"
call zmatrix_allocate(mat2,rows)
call zmatrix_set(mat2,rows,1.0,2.0)
call zmatrix_print(mat2,rows)
print *, "CCCCCC"
call zmatrix_allocate(mat3,rows)
mat3=zmat_zmat_add(mat1,mat2)
mat3=mat1+mat2
call zmatrix_print(mat3,rows)
call zmatrix_free(mat1)
call zmatrix_free(mat2)
call zmatrix_free(mat3)
End Program main
The comments point out the immediate problem - you don't need the dimension attribute in the zmat_zmat_add routine - you are adding a single matrix to another matrix, not an array of matrix to another array of matrices. Thus you have a scalar of the appropriate type for each dummy argument.
However as the actual question indicates there is a second problem, how to allocate the result array for zmat_zmat_add. Well, you make the result allocatable and allocate it! I've shown in the first code below the most direct way to solve the problems you are showing. However the code you have written reads a bit like writing C++ as Fortran. This is not the best way to solve this problem, and so I have provided a second solution which is a much more Fortran way of doing things. This is below the first code. Anyway here the quick and dirty fix to your code:
ijb#ijb-Latitude-5410:~/work/stack$ cat zm1.f90
module zmatrix_module
implicit none
type, public :: zcomplex_type
real :: realis
real :: imaginalis
end type zcomplex_type
type, extends(zcomplex_type), public :: zmatrix_type
type(zcomplex_type), dimension(:,:), allocatable, public :: zmatrix_data
end type zmatrix_type
public :: zmatrix_allocate
public :: zmatrix_free
public :: zmatrix_set
public :: zmatrix_print
interface operator(+)
procedure zzadd
procedure zmat_zmat_add
end interface
contains
function zzadd(z1,z2) result(res)
type(zcomplex_type), intent(in) :: z1
type(zcomplex_type), intent(in) :: z2
type(zcomplex_type) :: res
res%realis=z1%realis+z2%realis
res%imaginalis= z1%imaginalis +z2%imaginalis
end function zzadd
function zmat_zmat_add(zmatrix1,zmatrix2) result(res_zmat_zmat)
type(zmatrix_type), intent(in) :: zmatrix1
type(zmatrix_type), intent(in) :: zmatrix2
type(zmatrix_type) :: res_zmat_zmat
integer :: cols, rows
integer :: i,j
rows = Size( zmatrix1%zmatrix_data, Dim = 1 )
cols = Size( zmatrix1%zmatrix_data, Dim = 2 )
Allocate( res_zmat_zmat%zmatrix_data( 1:rows, 1:cols ) )
do i=1, rows
do j=1, cols
res_zmat_zmat%zmatrix_data(i,j)%realis= &
zmatrix1%zmatrix_data(i,j)%realis + zmatrix2%zmatrix_data(i,j)%realis
res_zmat_zmat%zmatrix_data(i,j)%imaginalis = &
zmatrix1%zmatrix_data(i,j)%imaginalis + &
zmatrix2%zmatrix_data(i,j)%imaginalis
enddo
enddo
end function zmat_zmat_add
subroutine zmatrix_allocate(zarray,rows)
type(zmatrix_type), intent(out) :: zarray
integer, intent(in) :: rows
allocate(zarray%zmatrix_data(1:rows, 1:rows))
end subroutine zmatrix_allocate
subroutine zmatrix_free(zarray)
type(zmatrix_type), intent(inout) :: zarray
deallocate(zarray%zmatrix_data)
end subroutine zmatrix_free
subroutine zmatrix_set(zarray, rows, re_values, im_values)
type(zmatrix_type), intent(inout) :: zarray
integer, intent(in) :: rows
real, intent(in) :: re_values, im_values
integer :: i,j
do i=1, rows
do j=1, rows
zarray%zmatrix_data(i,j)%realis = re_values
zarray%zmatrix_data(i,j)%imaginalis = im_values
enddo
enddo
end subroutine zmatrix_set
subroutine zmatrix_print(array,rows)
type(zmatrix_type), intent(in) :: array
integer, intent(in) :: rows
integer i,j
do i=1, rows
write(*,*) (array%zmatrix_data(i,j), j=1, rows)
enddo
write(*,*)
end subroutine zmatrix_print
end module zmatrix_module
Program main
use zmatrix_module
implicit none
type(zmatrix_type) :: mat1
type(zmatrix_type) :: mat2
type(zmatrix_type) :: mat3
integer :: rows
rows=2
print *, " AAAAAAA"
call zmatrix_allocate(mat1,rows)
call zmatrix_set(mat1,rows,10.0,8.0)
call zmatrix_print(mat1,rows)
print *, "BBBBBBBB"
call zmatrix_allocate(mat2,rows)
call zmatrix_set(mat2,rows,1.0,2.0)
call zmatrix_print(mat2,rows)
print *, "CCCCCC"
call zmatrix_allocate(mat3,rows)
mat3=zmat_zmat_add(mat1,mat2)
mat3=mat1+mat2
call zmatrix_print(mat3,rows)
call zmatrix_free(mat1)
call zmatrix_free(mat2)
call zmatrix_free(mat3)
End Program main
ijb#ijb-Latitude-5410:~/work/stack$ gfortran --version
GNU Fortran (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
ijb#ijb-Latitude-5410:~/work/stack$ gfortran -std=f2018 -Wall -Wextra -fcheck=all -O -Wuse-without-only zm1.f90 -o zm1
zm1.f90:99:4:
99 | use zmatrix_module
| 1
Warning: USE statement at (1) has no ONLY qualifier [-Wuse-without-only]
ijb#ijb-Latitude-5410:~/work/stack$ ./zm1
AAAAAAA
10.0000000 8.00000000 10.0000000 8.00000000
10.0000000 8.00000000 10.0000000 8.00000000
BBBBBBBB
1.00000000 2.00000000 1.00000000 2.00000000
1.00000000 2.00000000 1.00000000 2.00000000
CCCCCC
11.0000000 10.0000000 11.0000000 10.0000000
11.0000000 10.0000000 11.0000000 10.0000000
ijb#ijb-Latitude-5410:~/work/stack$
As I say this is not a very "Fortran" way of solving this. Here is what I would do. Note
The intrinsic Complex data type
Array syntax to simplify the code
Allocation of allocatable arrays when they are the result of a calculation, again simplifying the code
Use of intrinsics to find properties of arrays rather than carrying around extra variables which contain duplicate information
Type bound procedures
(Not really only Fortran but use of private for encapsulation and to minimise name space pollution)
Quite probably others
Anyway here goes
Module zmatrix_module
Implicit None
Type, Public :: zmatrix_type
Private
Complex, Dimension(:,:), Allocatable, Private :: zmatrix_data
Contains
Procedure, Public :: allocate => zmatrix_allocate
Procedure, Public :: free => zmatrix_free
Procedure, Public :: set => zmatrix_set
Procedure, Public :: print => zmatrix_print
Generic , Public :: Operator( + ) => add
Procedure, Private :: add => zmat_zmat_add
End Type zmatrix_type
Private
Contains
Function zmat_zmat_add(zmatrix1,zmatrix2) Result(res_zmat_zmat)
Class(zmatrix_type), Intent(in) :: zmatrix1
Type (zmatrix_type), Intent(in) :: zmatrix2
Type (zmatrix_type) :: res_zmat_zmat
! Uses allocation on assignment
! Also use array syntax to simplify code
res_zmat_zmat%zmatrix_data = zmatrix1%zmatrix_data + zmatrix2%zmatrix_data
End Function zmat_zmat_add
Subroutine zmatrix_allocate(zarray,rows)
! Note Intent(out) ensures the array is deallocate on entry to the routine
Class(zmatrix_type), Intent(out) :: zarray
Integer, Intent(in) :: rows
Allocate(zarray%zmatrix_data(1:rows, 1:rows))
End Subroutine zmatrix_allocate
Subroutine zmatrix_free(zarray)
Class(zmatrix_type), Intent(inout) :: zarray
Deallocate(zarray%zmatrix_data)
End Subroutine zmatrix_free
Subroutine zmatrix_set(zarray, values )
Class(zmatrix_type), Intent(inout) :: zarray
Complex, Intent(in) :: values
zarray%zmatrix_data = values
End Subroutine zmatrix_set
Subroutine zmatrix_print(array)
Class(zmatrix_type), Intent(in) :: array
Integer :: i
! Don't need to carry around extra data, just ask the array its size
Do i=1, Size( array%zmatrix_data, Dim = 1 )
Write(*,*) array%zmatrix_data(i,:)
Enddo
Write(*,*)
End Subroutine zmatrix_print
End Module zmatrix_module
Program main
Use zmatrix_module, Only : zmatrix_type
Implicit None
Type( zmatrix_type ) :: mat1
Type( zmatrix_type ) :: mat2
Type( zmatrix_type ) :: mat3
Integer :: rows
rows=2
Print *, " AAAAAAA"
Call mat1%allocate( rows )
Call mat1%set( ( 10.0, 8.0 ) )
Call mat1%print()
Print *, "BBBBBBBB"
Call mat2%allocate( rows )
Call mat2%set( ( 1.0, 2.0 ) )
Call mat2%print()
Print *, "CCCCCC"
! Note mat3 gets auto-allocated as a result of the operation
mat3 = mat1 + mat2
Call mat3%print()
Call mat3%free()
Call mat2%free()
Call mat1%free()
End Program main
ijb#ijb-Latitude-5410:~/work/stack$ gfortran --version
GNU Fortran (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
ijb#ijb-Latitude-5410:~/work/stack$ gfortran -std=f2018 -Wall -Wextra -fcheck=all -O -Wuse-without-only zm2.f90 -o zm2
ijb#ijb-Latitude-5410:~/work/stack$ ./zm2
AAAAAAA
(10.0000000,8.00000000) (10.0000000,8.00000000)
(10.0000000,8.00000000) (10.0000000,8.00000000)
BBBBBBBB
(1.00000000,2.00000000) (1.00000000,2.00000000)
(1.00000000,2.00000000) (1.00000000,2.00000000)
CCCCCC
(11.0000000,10.0000000) (11.0000000,10.0000000)
(11.0000000,10.0000000) (11.0000000,10.0000000)
ijb#ijb-Latitude-5410:~/work/stack$
There is another way which would fit this nicely, namely parametrised derived types. But I have no experience here so I won't go into something I don't know.
#Ian Bush mentioned in his answer that another option is to use the feature called parameterized derived types.
I will show a sample implementation here using those type parameters. Here are some callouts, first:
With this you might reduce the need for allocatable variables, because you can specify the shape of your type-members at compile-time or runtime.
If you want to learn more about parameterized types, this article is a good starting point.
This was introduced in Fortran 2003. The availability of this feature depends on the compiler. Even some compilers that claim to be fully compliant with Fortran 2003 might fail to compile (for example, gfortran fails in several points of the following code).
Sample implementation:
module zmatrix_module
implicit none
private
! A parameterized-type is declared like this.
type, public :: zmatrix(rows)
! Delcare each parameter inside the type. In this case it is a len-type.
integer, len :: rows
private
! You can use a len-type parameter in initialization expressions.
complex :: data(rows, rows)
contains
! No need for `allocate` and `free` procedures anymore.
procedure, public :: set => zmat_set
procedure, public :: print => zmat_print
procedure :: add => zmat_add_zmat
generic, public :: operator(+) => add
end type
contains
function zmat_add_zmat(z1, z2) result(out)
class(zmatrix(*)), intent(in) :: z1
type(zmatrix(*)), intent(in) :: z2
! Match the len parameter of the output with the input.
type(zmatrix(z1%rows)) :: out
out%data = z1%data + z2%data
end
subroutine zmat_set(z, values)
class(zmatrix(*)), intent(inout) :: z
complex, intent(in) :: values
z%data = values
end
subroutine zmat_print(z)
class(zmatrix(*)), intent(in) :: z
integer :: i
! You can inquiry the type parameter with a syntax similar to structure field.
do i=1, z%rows
write(*,*) z%data(i,:)
end do
write(*,*)
end
end
Sample program using:
program main
use zmatrix_module, only : zmatrix
implicit None
integer, parameter :: rows = 2
! A constant or initialization expression are allowed as the len parameter.
type(zmatrix(rows)) :: mat1
type(zmatrix(rows)) :: mat2
! If you declare an allocatable, you might declare the len parameter as deferred.
type(zmatrix(:)), allocatable :: mat3(:)
call mat1%set((10.0, 8.0))
call mat2%set((1.0, 2.0))
! Note mat3 gets auto-allocated as a result of the operation.
mat3 = mat1 + mat2
! You can also allocate the object explicitly, or using a source/mold.
! allocate(zmatrix(2) :: mat3)
! allocate(mat3, source=mat1)
call mat3%print()
end
The solution for this double integration is -0.083 but in the final compliation it appears -Infinity. It seems that the error is very simple, but I really can't find it.
I have been searching specially in the module section but I don't see why it appears like -Infinity. For example, if you change the two functions between them (x in f2 and x^2 in f1) the solution for the integration is 0.083 and the code gives it correct. Can annyone find the error? Thanks a lot.
module funciones
contains
function f(x,y)
implicit none
real*8:: x,y,f
f=2d0*x*y
end function
function f1(x)
real*8::x,f1
f1=x
end function
function f2(x)
real*8::x,f2
f2=x**2d0
end function
function g(x,c,d,h)
implicit none
integer::m,j
real*8::x,y,c,d,k,s,h,g
m=nint(((d-c)/h)+1d0)
k=(d-c)/dble(m)
s=0.
do j=1d0,m-1d0
y=c+dble(j)*k
s=s+f(x,y)
end do
g=k*(0.5d0*(f(x,c)+f(x,d))+s)
return
end function
subroutine trapecio(a,b,n,integral)
implicit none
integer::n,i
real*8::a,b,c,d,x,h,s,a1,a2,b1,b2,integral
h=(b-a)/dble(n)
s=0d0
do i=1d0,n-1d0
x=a+dble(i)*h
c=f1(x)
d=f2(x)
s=s+g(x,c,d,h)
end do
a1=f1(a)
a2=f2(a)
b1=f1(b)
b2=f2(b)
integral=h*(0.5d0*g(a,a1,a2,h)+0.5d0*g(b,b1,b2,h)+s)
end subroutine
end module
program main
use funciones
implicit none
integer::n,i
real*8::a,b,c,d,x,s,h,integral
print*, "introduzca los valores de a, b y n"
read(*,*) a, b, n
call trapecio (a,b,n,integral)
print*,integral
end program
The main program is simple, just calling the subroutine and using the module. It also prints the final result.
First of all, like mentioned in the comments: your problem is not clear. Which input parameters a, b and n do you use and which result do you expect?
Other than that: the code you posted used deprecated features and non-standard types and bad code style.
Some general hints:
real*8 is non-standard Fortran. Use real(real64) instead. real64 has to be imported by use :: iso_fotran_env, only: real64.
non-integer expressions (do i=1d0,n-1d0) in do-loops are a deleted feature in modern Fortran. Use integers instead.
code should be formatted with white spaces and indentations
print*, should be replaced with write(*,*)
code should always use English names
write implicit none in the beginning of the module, not for every function.
make the module/program interface clear by using the statements private, public, and only
if You want to convert to type real, use the function REAL instead of DBLE
I prefer the cleaner function definition using result
use intent keywords: intent(in) passes the variable as a const reference.
the variables c,d,x,s,h in the main program are unused. Compile with warnings to detect unused variables.
This is the code changed with the suggestions I made:
module funciones
use :: iso_fortran_env, only: real64
implicit none
private
public :: trapecio, r8
integer, parameter :: r8 = real64
contains
function f(x,y) result(value)
real(r8), intent(in) :: x,y
real(r8) :: value
value = 2._r8*x*y
end function
function f1(x) result(value)
real(r8), intent(in) :: x
real(r8) :: value
value = x
end function
function f2(x) result(value)
real(r8), intent(in) :: x
real(r8) :: value
value = x**2._r8
end function
function g(x,c,d,h) result(value)
real(r8), intent(in) :: x, c, d, h
real(r8) :: value
real(r8) :: y, k, s
integer :: m, j
m = NINT(((d-c)/h)+1._r8)
k = (d-c)/REAL(m, r8)
s = 0._r8
do j = 1, m-1
y = c + REAL(j,r8)*k
s = s + f(x,y)
end do
value = k*(0.5_r8*(f(x,c)+f(x,d))+s)
end function
subroutine trapecio(a, b, n, integral)
real(r8), intent(in) :: a, b
integer, intent(in) :: n
real(r8), intent(out) :: integral
integer :: i
real(r8) :: c, d, x, h, s, a1, a2, b1, b2
h = (b-a)/REAL(n,r8)
s = 0._r8
do i = 1, n-1
x = a + REAL(i,r8)*h
c = f1(x)
d = f2(x)
s = s + g(x,c,d,h)
end do
a1 = f1(a)
a2 = f2(a)
b1 = f1(b)
b2 = f2(b)
integral = h*(0.5_r8*g(a,a1,a2,h) + 0.5_r8*g(b,b1,b2,h) + s)
end subroutine
end module
program main
use funciones, only: trapecio, r8
implicit none
integer :: n,i
real(r8) :: a,b,integral
write(*,*) "introduzca los valores de a, b y n"
read(*,*) a, b, n
call trapecio (a,b,n,integral)
write(*,*) integral
end program
My problem is to pass the names of a series of functions contained in a module to a subroutine in a do loop.
I post part of my code. The modules are in two separate files compared to the main.
%%% FILE testKer_mod.f90
module kernel
implicit none
contains
! POLYNOMIAL
function poly(x, ts, ndim, a, param1, param2)
integer, intent (in) :: ndim
real*8, dimension(ndim), intent(in) :: x
real*8, dimension(ndim), intent(in) :: ts
real*8, intent(in) :: a, param1, param2
real*8 :: r
real*8 :: poly
r = (x(1:ndim) - ts(1:ndim))
poly = r**(.5*a)
end function poly
! GAUSSIAN
function gauss(x, ts, ndim, a, gamma, param2)
integer, intent (in) :: ndim
real*8, dimension(ndim), intent(in) :: x
real*8, dimension(ndim), intent(in) :: ts
real*8, intent(in) :: a, param2, gamma
real*8 :: r
real*8 :: gauss
r = (x(1:ndim) - ts(1:ndim))
gauss = exp(-(gamma*r)**a)
end function gauss
end module kernel
%%%
%%% FILE testSRBF_mod.f90
module srbf
implicit none
contains
subroutine weigth(nx, x, nts, ts, ndim, s, kernel, StocPar, param, coe, mat)
integer :: i,j,k,l,m,n
integer :: info
integer :: nx, nts, ndim
integer, dimension(nts) :: ipiv
real*8, dimension(nts) :: w
real*8, dimension(nts) :: s
real*8, dimension(2) :: param
real*8, dimension(nx,ndim) :: x
real*8, dimension(nts,ndim) :: ts
real*8, dimension(nx,nts) :: phi, mat
real*8, dimension(nts) :: coe
real*8 :: stocPar
interface
real*8 function kernel(x1, x2, n3, stov, p1, p2)
integer, intent (in) :: n3
real*8, dimension(n3), intent(in) :: x1
real*8, dimension(n3), intent(in) :: x2
real*8, intent(in) :: stov, p1, p2
end function kernel
end interface
do i = 1, nx
do j = 1, nts
phi(i,j) = kernel(x(i,1:ndim), ts(j,1:ndim), ndim, stocPar, param(1), param(2))
end do
end do
w = s
mat = phi
call DGESV(nts,1,mat,nts,ipiv,w,nts,info)
coe = w
end subroutine weigth
end module srbf
%%%
%%% MAIN PROGRAM test.f90
program MKRBF
use kernel
use srbf
implicit none
!real*8 :: SelKer
integer :: i,j,k
integer, parameter :: n = 3
integer, parameter :: nKer = 2
real*8, dimension(2,2) :: ParBound, auxpar
real*8, dimension(2) :: Bound
real*8, dimension(n) :: Var, func
real*8, dimension(n,nKer) :: coe
real*8, dimension(n,n) :: mat
!external SelKer
interface
real*8 function SelKer(ind)
integer, intent (in) :: ind
end function SelKer
end interface
Bound(1) = 0
Bound(2) = 5
ParBound(1,1) = 1
ParBound(1,2) = 5
ParBound(2,1) = 1
ParBound(2,2) = 5
auxpar(1,1) = 0
auxpar(1,2) = 0
auxpar(2,1) = 1
auxpar(2,2) = 1
var(:) = (/ 0., 2.5, 5. /)
do i = 1, n
func(i) = cos(3*Var(i)) * exp(-.25*Var(i));
end do
do i = 1, nKer
call weigth(n,Var,n,Var,1,func,SelKer(i),2.0D0,auxpar,coe,mat)
end do
end program MKRBF
function SelKer(indx)
integer, intent(in) :: indx
real*8 :: SelKer
select case (indx)
case (1)
SelKer = poly
case (2)
SelKer = gauss
end select
return
end function SelKer
%%%
I tried both with interface and with external but the program gives me the same error:
gfortran testKer_mod.f90 testSRBF_mod.f90 test.f90 -llapack -o test
test.f90:46:38:
call weigth(n,Var,n,Var,1,func,SelKer(i),2.0D0,auxpar,coe,mat)
1
Error: Expected a procedure for argument 'kernel' at (1)
How can I fix it?
Looking at the interface block in the main program
interface
real*8 function SelKer(ind)
integer, intent (in) :: ind
end function SelKer
end interface
SelKer is a function with real*8 result.1 SelKer(i) is such a function result. Crucially, SelKer(i) isn't a function with real*8 result, but such a real*8 value. weigth is expecting the argument for kernel to be a function (which is a procedure). This is the error message.
Coming to how the external function SelKer is implemented: we see such things as
SelKer = poly
In the function poly isn't the function poly in the module kernel but a local (default) real scalar variable (with undefined value). Note the lack of implicit none in the function.
Instead, you want to be looking to be using procedure pointers. This is a broad topic, so I'll just give an indication of the approach.
Move SelKer to be a procedure in the module kernel (removing the corresponding interface block from the main program).
Declare the function result SelKer to have type procedure(poly).
Use pointer assignment for the result, like SelKer => gauss.
There are other, perhaps better ways, to structure such a program. In particular, many would advise against using procedure pointer function results.
1 real*8 isn't standard Fortran.
This question already has answers here:
Why Segmentation fault is happening in this openmp code?
(2 answers)
Closed 6 years ago.
I have a very simple code.
program test_example
use iso_c_binding, only: c_double, c_int
implicit none
integer, parameter :: nelems = 500000
integer, parameter :: Np = 16, Nvar = 4, Nflux = 16
type mesh2d
real(c_double) :: u(Np, nelems)
real(c_double) :: uflux(Nflux, nelems)
real(c_double) :: ucommon(Nflux, nelems)
end type mesh2d
type(mesh2d) :: mesh
integer(c_int) :: i, j, k
!$OMP PARALLEL DO
do j = 1, nelems
do k = 1, Np
mesh%u(k, j) = j+k
end do
end do
!$END PARALLEL DO
end program test_example
I compile it using
gfortran -g temp.F90 -o main.exe -fopenmp
And it gives me segmentation fault. The same code runs fine if instead of using a derived type I simply used an array.
Is this a bug or am I doing something wrong.
I ran into your segfault conundrum on my laptop, but your code ran without a hitch on my powerful desktop machine. Your nelems = 500000 requires heap access. Following #Vladimir F suggestion I obtained the following:
This file was compiled by GCC version 5.4.0 20160609 using the options -cpp -imultiarch x86_64-linux-gnu -D_REENTRANT -mtune=generic -march=x86-64 -g -fopenmp
from
module type_Mesh2D
use iso_c_binding, only: &
wp => c_double, &
ip => c_int
! Explicit typing only
implicit none
! Everything is private unless stated otherwise
private
public :: wp, ip
public :: nelems, Np, Nvar, Nflux
public :: Mesh2D
integer (ip), parameter :: nelems = 500000
integer (ip), parameter :: Np = 16, Nvar = 4, Nflux = 16
type, public :: Mesh2D
real (wp), dimension (:,:), allocatable :: u, uflux, ucommon
end type Mesh2D
interface Mesh2D
module procedure allocate_arrays
module procedure default_allocate_arrays
end interface Mesh2D
contains
pure function allocate_arrays(n, m, k) result (return_value)
! Dummy arguments
integer (ip), intent (in) :: n, m, k
type (Mesh2D) :: return_value
allocate( return_value%u(n, m) )
allocate( return_value%uflux(k, m) )
allocate( return_value%ucommon(k, m) )
end function allocate_arrays
pure function default_allocate_arrays() result (return_value)
! Dummy arguments
type (Mesh2D) :: return_value
return_value = allocate_arrays(Np, nelems, Nflux)
end function default_allocate_arrays
end module type_Mesh2D
program test_example
use iso_fortran_env, only: &
compiler_version, compiler_options
use type_Mesh2D
! Explicit typing only
implicit none
type (Mesh2D) :: mesh
integer (ip) :: i, j, k
! Allocate memory
mesh = Mesh2D()
!$OMP PARALLEL DO
do j = 1, nelems
do k = 1, Np
mesh%u(k, j) = j + k
end do
end do
!$END PARALLEL DO
print '(4A)', &
'This file was compiled by ', compiler_version(), &
' using the options ', compiler_options()
end program test_example
Over 2 weeks, I've struggled to call one of the METIS library written in C from my fortran code. And, unfortunately, It doesn't seem to be a HAPPY END without your help. I found some posts about direct calling and using interface. I prefer the latter because I could monitor the variables for debugging. There are three codes I attached.
1. c function I'd like to use 2. fortran interface module 3. fortran program
(1) c function
int METIS_PartMeshNodal(idx_t *ne, idx_t *nn, idx_t *eptr, idx_t *eind,
idx_t *vwgt, idx_t *vsize, idx_t *nparts, real_t *tpwgts,
idx_t *options, idx_t *objval, idx_t *epart, idx_t *npart)
I removed the c funciton body. It's not necessary to understand my problem
Here, idx_t is integer and real_t is single or double precision. From ne to options are input and last three arguments are output. And vwgt, vsize, tpwgts and options can receive null as an input for default setting I wrote the interface module for using c function like this
(2) Fortran interface module
Fixed!
Insert use iso_c_bind under use constants
Use integer(c_int) instead of integer for ne, nn and other variables.
Remove unused module constants
.
module Calling_METIS
!use constants, only : p2 !this is for double precision
use iso_c_bind !inserted later
implicit none
!integer :: ne, nn !modified
integer(c_int) :: ne, nn
!integer, dimension(:), allocatable :: eptr, eind !modified
integer(c_int), dimension(:), allocatable :: eptr, eind
!integer, dimension(:), allocatable :: vwgt, vsize !modified
type(c_ptr) :: vwgt, vsize
!integer :: nparts !modified
integer(c_int) :: nparts
!real(p2), dimension(:), allocatable :: tpwgts !modified
type(c_ptr) :: tpwgts
!integer, dimension(0:39) :: opts !modified
integer(c_int), dimension(0:39) :: opts
!integer :: objval !modified
integer(c_int) :: objval
!integer, dimension(:), allocatable :: epart, npart !modified
integer(c_int), dimension(:), allocatable :: epart, npart
interface
subroutine METIS_PartMeshNodal( ne, nn, eptr, eind, vwgt, vsize, nparts, tpwgt, &
opts, objval, epart, npart) bind(c)
use intrinsic :: iso_c_binding
!use constants, only : p2
implicit none
integer (c_int), intent(in) :: ne, nn
integer (c_int), dimension(*), intent(in) :: eptr, eind
!integer (c_int), dimension(*), intent(in) :: vwgt, vsize !modified
type(c_ptr), value :: vwgt, vsize
integer (c_int), intent(in) :: nparts
!real(c_double), dimension(*), intent(in) :: tpwgt !modified
type(c_ptr), value :: tpwgt
integer (c_int), dimension(0:39), intent(in) :: opts
integer (c_int), intent(out) :: objval
integer (c_int), dimension(*), intent(out) :: epart
integer (c_int), dimension(*), intent(out) :: npart
end subroutine METIS_PartMeshNodal
end interface
end module
And here is my program code calling the function
(3) Fortran program
Fixed!
allocation size of npart is fixed. Not ne but nn
opts(7)=1 is added to get Fortran-style array of epart, npart(no effect until now)
.
program METIS_call_test
!some 'use' statments
use Calling_METIS
use iso_c_binging !added
implicit none
! Local variable
integer :: iC
character(80) :: grid_file !grid_file
grid_file = 'test.grid'
! (1) Read grid files
call read_grid(grid_file)
! (2) Construction Input Data for calling METIS Function
! # of cells, vertices
ne = ncells
nn = nvtxs
! eptr, eind allocation
allocate(eptr(0:ne), eind(0:3*ntria + 4*nquad - 1))
! eptr and eind building
eptr(0) = 0
do iC=1, ncells
eptr(iC) = eptr(iC-1) + cell(iC)%nvtxs
eind(eptr(iC-1):eptr(iC)-1) = cell(iC)%vtx
end do
! epart, npart building
!allocate(epart(ne), npart(ne))
allocate(epart(ne), npart(nn)) ! modified
! # of partition setting
nparts = 2
vwgt = c_null_ptr !added
vsize = c_null_ptr !added
tpwgt = c_null_ptr !added
! (3) Call METIS_PartMeshNodal
call METIS_SetDefaultOptions(opts)
opts(7) = 1 !Added. For fortran style output array epart, npart.
call METIS_PartMeshNodal(ne, nn, eptr, eind, vwgt, vsize, nparts, tpwgt, &
opts, objval, epart, npart)
!call METIS_PartMeshNodal(ne, nn, eptr, eind, null(), null(), nparts, null(), &
! opts, objval, epart, npart) !wrong...
end program
But the problem is that I get an error message as below though I put null for tpwgt.
Input Error: Inorrect sum of 0.000000 for tpwgts for constraint 0.
And this message is handled in the code below.
for (i=0; i<ctrl->ncon; i++) {
sum = rsum(ctrl->nparts, ctrl->tpwgts+i, ctrl->ncon);
if (sum < 0.99 || sum > 1.01) {
IFSET(dbglvl, METIS_DBG_INFO,
printf("Input Error: Incorrect sum of %"PRREAL" for
tpwgts for constraint %"PRIDX".\n", sum, i));
return 0;
}
}
Anyway, in order to see what I would get if I put an array for tpwgts intead of null, tpwgts(:) = 1.0/nparts, which makes sum of tpwgts equal 1.0. But I got same message with 1.75 for the sum.
These are my questions
1. Did I use null() for passing arguments correctly?
2. Do I have to pass pointers for all arguments to c function? then how?
3. Is putting an integer to opts(0:39) enough for use? For example, in a post without 'interface module', simple code like options(3)=1 is used. But in the c code, options has 16 named variable like options[METIS_OPTION_NUMBERING], options[METIS_OPTION_UFACTOR]. I think some thing is necessary to set options but I have no idea.
4. Is there an example for METIS in fortran?
Any kind of hint/advice will be a great help for me. Thank you.
Conclution
The problem I had was that c function couldn't recognize null pointer from fortran code.
There were some miss declations of variables in interface module(see 'Fixed' and comments)
It looks like the code works properly. But option(7) = 1 for fortran style output didn't work and now I'm looking at it.
No, you cannot pass null(), that is a Fortran pointer constant. You must pass C_NULL_PTR from the module ISO_C_BINDING and the interface must reflect this. The dummy argument must be type(c_ptr), most probably with VALUE attribute. It may actually work because of the same internal representation, but I wouldn't count on it.
No, if you pass some normal variable, you can pass it directly by reference. Just like normally in Fortran. If the interface is BIND(C), the compiler knows it must send a pointer.
There is a new TS to update Fortran 2008, where you can define dummy arguments in the interoperable procedures as OPTIONAL. Then you can pass the null pointer just by omitting them. Gfortran should already support this.
Note: Here I can see a much different C signature of your function, are you sure yours is OK? http://charm.cs.uiuc.edu/doxygen/charm/meshpart_8c.shtml
I think your opts(7) does not work because you also need an interface for the METIS function METIS_SetDefaultOptions. Based on the answer from http://glaros.dtc.umn.edu/gkhome/node/877, I created a wrapper module (metisInterface.F90) with the interfaces I needed:
module metisInterface
! module to allows us to call METIS C functions from the main Fortran code
use,intrinsic :: ISO_C_BINDING
integer :: ia,ic
integer(C_INT) :: metis_ne,metis_nn
integer(C_INT) :: ncommon,objval
integer(C_INT) :: nparts
integer(C_INT),allocatable,dimension(:) :: eptr,eind,perm,iperm
integer(C_INT),allocatable,dimension(:) :: epart,npart
type(C_PTR) :: vwgt,vsize,twgts,tpwgts
integer(C_INT) :: opts(0:40)
interface
integer(C_INT) function METIS_SetDefaultOptions(opts) bind(C,name="METIS_SetDefaultOptions")
use,intrinsic :: ISO_C_BINDING
implicit none
integer(C_INT) :: opts(0:40)
end function METIS_SetDefaultOptions
end interface
interface
integer(C_INT) function METIS_PartMeshDual(ne,nn,eptr,eind,vwgt,vsize,ncommon,nparts,tpwgts, &
opts,objval,epart,npart) bind(C,name="METIS_PartMeshDual")
use,intrinsic :: ISO_C_BINDING
implicit none
integer(C_INT):: ne, nn
integer(C_INT):: ncommon, objval
integer(C_INT):: nparts
integer(C_INT),dimension(*) :: eptr, eind
integer(C_INT),dimension(*) :: epart, npart
type(C_PTR),value :: vwgt, vsize, tpwgts
integer(C_INT) :: opts(0:40)
end function METIS_PartMeshDual
end interface
end module metisInterface
Then, in the main program (or wherever you make the call to the METIS functions) you need to have (for completeness, I also added the call to METIS_PartMeshDual):
use metisInterface
integer :: metis_call_status
.
.
.
metis_call_status = METIS_SetDefaultOptions(opts)
! METIS_OPTION_NUMBERING for Fortran
opts(17) = 1
metis_call_status = METIS_PartMeshDual(metis_ne,metis_nn,eptr,eind, &
vwgt,vsize,ncommon,nparts,tpwgts,opts,objval,epart,npart)
Note that epart and npart will have Fortran numbering as you want (starting at 1). However, the processors will also start numbering at 1. For example, if you are running in 4 processors, root processor is 1 and you may have epart(n)=4, and you will not have any epart(n)=0.
Finally, a file metis.c is also needed with a single line:
#include "metis.h"
Compiling instructions
Compile metis.c with a C compiler
Compile metisInterface.F90 with a Fortran compiler linking with the compiled C object
Compile main program with a Fortran compiler linking with metisInterface.o