I am trying to code a computationally efficient PACK operation over a polymorphic array and I am running on issues with gfortran 9.2.0:
The PACK operation has to work on a polymorphic array of a derived type quantity, and return a result on itself
For reasons I'm not explaining here, this array should not be reallocated
In general, there is overlap between the locations of the returned indices, and those of the original array: something like array(1:5) = array([2,4,6,8,10])
I'm having problems, as the only version of the assigment I've tried with gfortran is with a loop - all array-based version either produce compiler or runtime segfaults.
An example is reported in this program:
module m
implicit none
type, public :: t
integer :: i = 0
contains
procedure, private, pass(this) :: t_assign => t_to_t
generic :: assignment(=) => t_assign
end type t
type, public, extends(t) :: tt
integer :: j = 0
contains
procedure, private, pass(this) :: t_assign => t_to_tt
end type tt
contains
elemental subroutine t_to_t(this,that)
class(t), intent(inout) :: this
class(t), intent(in ) :: that
this%i = that%i
end subroutine t_to_t
elemental subroutine t_to_tt(this,that)
class(tt), intent(inout) :: this
class(t ), intent(in ) :: that
this%i = that%i
select type (thatPtr=>that)
type is (t)
this%j = 0
type is (tt)
this%j = thatPtr%j
class default
! Cannot stop here
this%i = -1
this%j = -1
end select
end subroutine t_to_tt
end module m
program test_poly_pack
use m
implicit none
integer, parameter :: n = 100
integer :: i,j
class(t), allocatable :: poly(:),otherPoly(:)
allocate(t :: poly(n))
allocate(t :: otherPoly(10))
! Assign dummy values
forall(i=1:n) poly(i)%i = i
! Array assignment with indices => ICE segfault:
! internal compiler error: Segmentation fault
otherPoly(1:10) = poly([10,20,30,40,50,60,70,80,90,100])
! Scalar assignment with loop -> OK
do i=1,10
otherPoly(i) = poly(10*i)
end do
! Array assignment with PACK => Compiles OK, Segfault on runtime. GDB returns:
! Thread 1 received signal SIGSEGV, Segmentation fault.
! 0x000000000040163d in m::t_to_t (this=..., that=...) at test_poly_pack.f90:31
! 31 this%i = that%i
otherPoly(1:10) = pack(poly,mod([(j,j=1,100)],10)==0)
do i=1,10
print *, ' polymorphic(',i,')%i = ',otherPoly(i)%i
end do
end program test_poly_pack
Am I doing anything wrong, and/or is this only a compiler bug or there is any best practices I should be following?
The crashes are compiler bugs. When the compiler says internal compiler error ... Please submit a full bug report, you really can trust it and you should act accordingly (and submit the bug report). The runtime crash is a compiler bug as well (wrong code).
If you know the actual types at the time of the assignment, you can use type guards
select type (p => poly)
type is (t)
select type(op => otherpoly)
type is (t)
op(1:10) = pack(p,mod([(j,j=1,100)],10)==0)
end select
end select
If you need it to be polymorphic - you probably have to reallocate
allocate(otherPoly(1:10),source = pack(poly,mod([(j,j=1,100)],10)==0))
until the bugs you hopefully reported are fixed.
Related
Is there possibility to use indexing directly on a function's return value? Something like this:
readStr()(2:5)
where readStr() is a function which returns a character string or an array. In many other languages it is quite possible, but what about Fortran? The syntax in my example of course does not compile. Is there any other syntax to be used?
No, that is not possible in Fortran. You could, however, alter your function to take an additional index array that determines which elements are returned. This example illustrates this possibility using an interface to allow for an optional specification of the indices (simplified greatly thanks to the comment by IanH):
module test_mod
implicit none
contains
function squareOpt( arr, idx ) result(res)
real, intent(in) :: arr(:)
integer, intent(in), optional :: idx(:)
real,allocatable :: res( : )
real :: res_( size(arr) )
integer :: stat
! Calculate as before
res_ = arr*arr
if ( present(idx) ) then
! Take the sub-set
allocate( res(size(idx)), stat=stat )
if ( stat /= 0 ) stop 'Cannot allocate memory!'
res = res_(idx)
else
! Take the the whole array
allocate( res(size(arr)), stat=stat )
if ( stat /= 0 ) stop 'Cannot allocate memory!'
res = res_
endif
end function
end module
program test
use test_mod
implicit none
real :: arr(4)
integer :: idx(2)
arr = [ 1., 2., 3., 4. ]
idx = [ 2, 3]
print *, 'w/o indices',squareOpt(arr)
print *, 'w/ indices',squareOpt(arr, idx)
end program
No.
But if it bothers you, you can write your own user defined functions and operators to achieve a similar outcome without having to store the result of the function reference in a separate variable.
You can avoid declaring another variable if you use associate. Whether it is any better or clearer than a temporary variable must be decided by the user. The result has to be stored somewhere anyway.
associate(str=>readStr())
print *, str(2:5)
end associate
It will not be very useful for this specific case with a potentially long string but might be more useful for other similar cases that get linked here as duplicates.
I am having a problem trying to use subsets of an array containing instances of a type in Fortran. Creating the subset renders the contents garbage. Essentially it boils down to:
class(myType), allocatable :: instances(:)
...allocate/initialize instances here...
doSomethingWithInstances( instances ) ! Works
doSomethingWithInstances( instances((/1,2/)) ) ! Doesn't work
Here is a complete code that reproduces the problem, in this code the correct output is the values of the integers assigned to each of the instances i.e "3, 8, 16", when the subroutine is called on the subset (/1,2/) of the array it should therefore print "3, 8" however it instead prints "3, 192552":
module test
! Simple type contains integer
type :: myType
integer :: n
end type
contains
! Output the integer for myType t
subroutine saySomething(t)
class(myType) :: t
print *, t%n
end subroutine
end module
program main
use test
type(myType), allocatable :: instances(:)
! Declare an array of myType
allocate(instances(3))
instances(1) = myType(3)
instances(2) = myType(8)
instances(3) = myType(16)
! call saySomething for each element
! on the direct array and on the subset
! elements 1 and 2
print *, "Working:"
call saySomethingArray(instances)
print *, "Broken:"
call saySomethingArray(instances((/1,2/))) ! Here is the problem
contains
! Call saySomething on each element of the input array
subroutine saySomethingArray(instances)
class(myType) :: instances(:)
integer :: i
do i=1,size(instances)
call saySomething(instances(i))
enddo
end subroutine
end program
I wondered if it wasn't copying things correctly or something when it creates the subset, but I couldn't figure it out. Any help would be greatly appreciated :) cheers
In these cases please always tell us what is your compiler, its version and all flags used for compiling. This is a compiler bug. I can reproduce it with GCC 7. It runs correctly with Intel Fortran 16.
As a workaround, the error will go away if you declare instances in saySomethingArray as type instead of class. Also, printing the array as an array expression works correctly.
The problem also appears for other kinds of array expressions, not just vector subscripts.
Reported to GCC as https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84074. Maybe it will turn up to be a duplicate of some earlier bug. This is the MCVE ([mcve]):
type :: t
integer :: n
end type
type(t) :: array(2) = [t(1),t(2)]
call sub(array((/1,2/)))
contains
subroutine sub(a)
class(t) :: a(:)
integer :: i
print *, "loop a(i) :"
do i=1,size(a)
print *,a(i)%n
enddo
print *, "a%n :",a%n
print *, "a(1:size(a))%n :",a(1:size(a))%n
end subroutine
end program
Output:
> gfortran-7 vecsubs2.f90
> ./a.out
loop a(i) :
2
0
a%n : 1 2
a(1:size(a))%n : 1 2
The goal is to create a single allocation routine which can handle any type of rank one allocation. Our code library can then have a single call with standardized error trapping.
The compiler error follows:
generic_allocation.f08:32:27:
call myAllocator ( array_int, source_int, lambda )
1
Error: Actual argument to ‘myarray’ at (1) must be polymorphic
generic_allocation.f08:33:27:
call myAllocator ( array_real, source_real, lambda )
1
Error: Actual argument to ‘myarray’ at (1) must be polymorphic
Can this code be rectified?
The test code attempts to allocate an integer array and then a real array:
module mAllocator
implicit none
contains
subroutine myAllocator ( myArray, source_type, lambda )
class ( * ), allocatable, intent ( inout ) :: myArray ( : )
class ( * ), intent ( in ) :: source_type
integer, intent ( in ) :: lambda
integer :: alloc_status = 0
character ( len = 512 ) :: alloc_message = ''
allocate ( myArray ( 1 : lambda ), source = source_type, stat = alloc_status, errmsg = alloc_message )
if ( alloc_status /= 0 ) then
write ( *, "( ' allocation errmsg = ', g0, '.' )" ) trim ( alloc_message )
stop 'Fatal error in subroutine myAllocator'
end if
end subroutine myAllocator
end module mAllocator
program generic_allocation
use mAllocator, only : myAllocator
implicit none
integer, parameter :: lambda = 10
integer, parameter :: source_int = 1
real, parameter :: source_real = 1.0
integer, allocatable :: array_int ( : )
real, allocatable :: array_real ( : )
call myAllocator ( array_int, source_int, lambda )
call myAllocator ( array_real, source_real, lambda )
end program generic_allocation
The first version of the code relied upon a select type construct as shown in FORTRAN: polymorphism allocation. Another reference used is Fortran polymorphism, functions and allocation.
The gfortran version is 6.0
$ gfortran -v
Using built-in specs.
COLLECT_GCC=gfortran
COLLECT_LTO_WRAPPER=/opt/gnu/6.0/libexec/gcc/x86_64-pc-linux-gnu/6.0.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: ./configure --prefix=/opt/gnu/6.0 --enable-languages=c,c++,fortran,lto --disable-multilib --disable-werror
Thread model: posix
gcc version 6.0.0 20160227 (experimental) (GCC)
You are encountering a deliberate restriction in the language, put in place to prevent a procedure from allocating an object to some type that does not match the declared type of the actual argument. Consider what would happen if your allocator allocated the dummy argument corresponding to array_int to be of type REAL.
You cannot achieve your goal with a single procedure, however you may be able to get away with writing a single stretch of source code, that you then INCLUDE into the body of multiple procedures, one for each declared type (and kind) that you wish to deal with.
! In AllocateBody.i90
integer, intent(in) :: lambda
integer :: alloc_status
character ( len = 512 ) :: alloc_message
allocate ( myArray ( 1 : lambda ), &
source = source_type, &
stat = alloc_status, &
errmsg = alloc_message )
if ( alloc_status /= 0 ) then
write ( *, "( ' allocation errmsg = ', g0, '.' )" ) &
trim ( alloc_message )
stop 'Fatal error in subroutine myAllocator'
end if
! Elsewhere.
subroutine my_allocator_integer(myArray, source_type, lambda )
integer, intent(out), allocatable :: myArray(:)
integer, intent(in) :: source_type
include 'AllocateBody.i90'
end subroutine my_allocator_integer
subroutine my_allocator_real(myArray, source_type, lambda )
real, intent(out), allocatable :: myArray(:)
real, intent(in) :: source_type
include 'AllocateBody.i90'
end subroutine my_allocator_real
subroutine my_allocator_foo(myArray, source_type, lambda )
type(foo), intent(out), allocatable :: myArray(:)
type(foo), intent(in) :: source_type
include 'AllocateBody.i90'
end subroutine my_allocator_foo
You could put all these specific procedures behind the one generic name.
However, before you embark on this, note that in modern Fortran that allocatable things can be allocated even without an ALLOCATE statement - simple assignment to an allocatable variable can result in it being ALLOCATED. You have no way of handling error messages for those cases. There are also a very large number of coding constructs that will result in the compiler "allocating" memory for its own internal needs, which again, you have no way of handling errors for. At a lower level, the way that operating systems actually fulfil requests by program for memory is also working against you - the system may be overcommitted, and an insufficient memory error may not be reported by the operating system to the process until well after an allocate statement had completed. In combination, in the situation where available memory is very low and an attempt to allocate a small object has failed, it is possible that there is insufficient memory available for the compiler to even execute your error reporting code. There is also the issue that the compiler's runtime has a better idea of the reasons for failure and the state of the program that it can communicate via a simple integer code and a character message - for example the compiler's runtime can give the user a stack trace or similar, in addition to whatever message it could otherwise pass back to the program.
All up, for small allocations, programmer provided error reporting may not be very productive.
It can be very worthwhile for larger allocations, where the probability of specific failure is higher and it is very likely that the reason can be successfully communicated and acted on ("Your problem dimension is too big! Please make it smaller and try again...") by a user.
i have allocated a lot of 2D arrays in my code, and I want each one array to read from a file named as array's name. The problem is that each array has different size, so I am looking for the most efficient way. The code is like this:
Module Test
USE ...
implicit NONE
private
public:: initializeTest, readFile
real(kind=8),dimension(:,:),allocatable,target:: ar1,ar2,ar3,ar4,ar5,...,ar10
real(kind=8),dimension(:,:),pointer:: pAr
CONTAINS
!
subroutine initializeTest
integer:: k1,k2,k3,k4,k5
integer:: ind1,ind2
allocate(ar1(k1,k1),ar2(k1,k2),ar3(k2,k4),ar4(k5,k5),...) !variable sizes
! here needs automatization - since its repeated
pAr => ar1
ind1 = size(pAr,1)
ind2 = size(pAr,2)
call readFile(par,ind1,ind2)
pAr => ar2
ind1 = size(pAr,1)
ind2 = size(pAr,2)
call readFile(par,ind1,ind2)
!....ar3, ... , ar9
pAr => ar10
ind1 = size(pAr,1)
ind2 = size(pAr,2)
call readFile(par,ind1,ind2)
end subroutine initializeTest
!
!
subroutine readFile(ar,row,col)
real(kind=8),dimension(row,col)
integer:: i,j,row,col
! it should open the file with same name as 'ar'
open(unit=111,file='ar.dat')
do i = 1, row
read(222,*) (ar(i,j),j=1,col)
enddo
end subroutine importFile
!
!
end module Test
If your arrays ar1, ar2, etc. had the same dimensions you could put them all in a 3-dimensional array. Since they have different dimensions, you can define a derived type, call it a "matrix", with an allocatable array component and then create an array of that derived type. Then you can read the i'th matrix from a file such as "input_1.txt" for i=1.
The program below, which works with g95 and gfortran, shows how the derived type can be declared and used.
module foo
implicit none
type, public :: matrix
real, allocatable :: xx(:,:)
end type matrix
end module foo
program xfoo
use foo, only: matrix
implicit none
integer, parameter :: nmat = 9
integer :: i
character (len=20) :: fname
type(matrix) :: y(nmat)
do i=1,nmat
allocate(y(i)%xx(i,i))
write (fname,"('input_',i0)") i
! in actual code, read data into y(i)%xx from file fname
y(i)%xx = 0.0
print*,"read from file ",trim(fname)
end do
end program xfoo
As far as I know, extracting the name of the variable from the variable at runtime isn't going to work.
If you need lots of automation for the arrays, consider using an array of a derived type, as one other answer suggests, in order to loop over them both for allocation and reading. Then you can enumerate the files, or store a label with the derived type.
Sticking to specific array names, an alternative is to just read/write the files with the required name as an argument to the routine:
Module Test
...
! here needs automatization - since its repeated
call readFile(ar1,'ar1')
call readFile(ar2,'ar2')
!....ar3, ... , ar9
call readFile(ar10,'ar10')
end subroutine initializeTest
subroutine readFile(ar,label)
real(kind=8) :: ar(:,:)
character(len=*) :: label
integer:: i,j,nrow,ncol,fd
nrow=size(ar,1)
ncol=size(ar,2)
open(newunit=fd,file=label)
do i = 1, row
read(fd,*) (ar(i,j),j=1,col)
enddo
end subroutine readFile
end module Test
Some unsollicited comments: I don't really get why (in this example) readFile is public, why the pointers are needed? Also, kind=8 shouldn't be used (Fortran 90 kind parameter).
Befor finding an answer for the quesion I asked before, I wrote very simple code for very sime mesh(there are just two triangles) as below to call C function in fortran. For the simplicity of code. Neither interface module here nor iso_c_binding(as possible as) isn't used here. It is very similar to this post, There is another error.
program METIS_NoInterface
implicit none
integer :: ne, nn
integer, dimension(:), allocatable :: eptr, eind
integer, pointer :: vwgt=>null(), vsize=>null()
integer :: nparts
real, pointer :: tpwgts=>null()
integer, dimension(0:39) :: opts
integer :: objval
integer, dimension(:), allocatable :: epart, npart
! Input => 2 tri: too small mesh
!ne = 2
!nn = 4
!nparts = 2
!allocate(eptr(0:2), eind(0:5))
!eptr=[0, 3, 4]
!eind=[0,1,3,1,2,3]
! Output
!allocate(epart(0:1), npart(0:4))
! Input => 4 quad : reasonable result
ne = 4
nn = 9
nparts = 2
allocate(eptr(0:ne), eind(0:15))
eptr=[0,4,8,12,16]
eind=[0,1,8,7,1,2,3,8,3,4,5,8,5,6,7,8]
! Output
allocate(epart(0:ne-1), npart(0:nn-1))
! METIS function call
call METIS_SetDefaultOptions(opts)
!call METIS_PartMeshNodal(ne,ne,eptr,eind,vwgt,vsize,nparts,tpwgts,& !<=syntax error
call METIS_PartMeshNodal(ne,nn,eptr,eind,vwgt,vsize,nparts,tpwgts,&
opts,objval,epart,npart)
! Result print
write(*,*) epart
write(*,*) ''
write(*,*) npart
write(*,*) ''
end program
It gives access violation error because of eind. It runs anyway if c_null_ptr are passed instead of eind. The size of array eind and number of elements are coincident. How can it be fixed? => resolved. Thank you!
The problem I had was systax error on calling METIS_PartMeshNodal, the 2nd argument was ne which is the same the 1st argument ne. It should have been nn.
Solution was to replace the 2nd argument to nn. Above code can be compiled and executed.
NOTE: Too small mesh may not have reasonable solution due to METIS' scheme.
Please refer another post in order to use interface module.