0

I am developing a Fortran 90 program and am linking it with METIS libraries. I am using GNU Fortran 4.8.5 and METIS 5.1.0. I compile my Fortran program with:

-fdefault-real-8 -fdefault-integer-8

options, and when building METIS I specified:

#define IDXTYPEWIDTH 64 

and

#define REALTYPEWIDTH 64 

hence the length of both integers and reals should fit. When I compile and run the following program:

program Test_Metis
implicit none
integer              :: nvtxs,          &  ! number of vertices
                        ncons,          &  ! number of connections          
                        nparts = 2         ! requested number of partitions
integer, pointer     :: vwgt  =>null(), &  ! weights of the vertices
                        vsize =>null(), &  ! size of the vertices
                        adjwgt=>null(), &  ! weights of the edges
                        mopts =>null(), &  ! array of options
                        objval=>null()     ! stores edgecut or comm. volume
real, pointer        :: tpwgts=>null(), &  ! desired weight for each partition
                        ubvec =>null()     ! 
integer, allocatable :: xadj  (:),      &  ! variabes for ...
                        adjncy(:),      &  ! ... compressed row storage
                        part  (:)          ! partition of the grid

nvtxs = 15
ncons = 22
allocate(xadj  (nvtxs+1))
allocate(adjncy(ncons*2))
allocate(part  (nvtxs))

xadj = (/ 0,  2,  5,  8, 11, 13, 16, 20,  &
         24, 28, 31, 33, 36, 39, 42, 44/)

adjncy=(/ 1,  5,  0,  2,  6,  1,  3,  7,  2,  4,  8,  &
          3,  9,  0,  6, 10,  1,  5,  7, 11,  2,  6,  &
          8, 12,  3,  7,  9, 13,  4,  8, 14,  5, 11,  &
          6, 10, 12,  7, 11, 13,  8, 12, 14,  9, 13/)

call METIS_PartGraphRecursive(nvtxs,     &  ! (in), int
                              ncons,     &  ! (in), int
                              xadj,      &  ! (in), int(:)
                              adjncy,    &  ! (in), int(:)
                              vwgt,      &  ! (in), int(:)
                              vsize,     &  ! (in), int(:)
                              adjwgt,    &  ! (in), int(:)
                              nparts,    &  ! (in), int(:)
                              tpwgts,    &  ! (in), real(:)
                              ubvec,     &  ! (in), real(:)
                              mopts,     &  ! (in), int(:)
                              objval,    &  ! (out) int(:)
                              part)         ! (out) int(:)
end program

I get a segmentation fault. (I took adjacency from the METIS manual, that should be pretty basic.)

Can anyone help me out with this issue?

Bojan Niceno
  • 113
  • 1
  • 1
  • 11
  • Without looking at the API documentation: are you sure you are allowed to have the arguments (such as `ubvec`) as not-associated pointers? – francescalus Aug 19 '18 at 16:53
  • If you use gfortran's -fdefault-* options to compile your code, then you need to compile all libraries you link with your code with those options. These options break Fortran's storage association rules. What happens if you don't use those options. – evets Aug 19 '18 at 17:09
  • 1
    I'm inclined to believe that [this other question](https://stackoverflow.com/q/14730349/3157076) has an answer addressing your problem. Please check. – francescalus Aug 19 '18 at 17:14
  • Dear @francescalus: thanks for the answer. Indeed, according to API documentation, one is supposed to send null pointers for data which is not used by the routines. I also followed your link and advice there. Using c_iso_binding, c_ptr and c_null_ptr got me further, but did not resolve all the issues. Eventually, I am sending all the arguments in the extended form required by API and all works. – Bojan Niceno Aug 21 '18 at 15:14
  • Dear @evets thanks for the answer, but I am quite consistent with -fdefault options. I am also very careful to use the same lengths for float and integers. – Bojan Niceno Aug 21 '18 at 15:19
  • Using _C_ null pointers may be possible, but (unless the Fortran API is written specifically to accept them) a disassociated Fortran pointer is not the same thing as a C null pointer. One answer to that other question looks at using `c_null_ptr` (a Fortran constant with value of C's NULL) but some further work would be required. – francescalus Aug 21 '18 at 15:20
  • @Bojan Niceno, as the person who introduced the -fdefault-* options into gfortran (actually renamed -i8 and -r8 and made them work for some definition of work), I am fairly certain that my advice to avoid these options is sound. In particular, these options have problems when one tries to interface with C. – evets Aug 21 '18 at 15:49
  • @evets: respect, but you just shattered my favorite Fortran options :-( I am a scientist who programs to solve scientific problems. Said that, for me it is a waste of time to think of each variable: "Oh, shall it be short or long int? Signed or unsigned? What about that float? Is single precision enough?" No matter how these questions may be intriguing, I can't afford them to occupy my focus. Instead, I just want floats with highest precision, and integers with broadest range. If -fdefaults are not good, what would you suggest instead? Use integer(8) and real(8) throughout the code? – Bojan Niceno Aug 21 '18 at 16:16
  • @BojanNiceno Use in a general include file a definition for the kind of integers / floats to be used and use this kind in the declarations (makes changing / switching easy). Also look for options to automatically create interfaces and check against them (like the Intel options: -gen_interfaces -warn interfaces). Don't use real(8) as this is not portable! – albert Aug 21 '18 at 17:24
  • @BojanNiceno, follow Albert's advice by creating a types module to define the kind type parameter. Use that module everywhere. You can then flip precision by changing a few lines of code and recompiling. – Steve Aug 21 '18 at 17:44
  • @BojanNiceno, if you think you need to use the -fdefault-* options, then look into using the -freal-4-real-8 family of options. The -freal-* options don't muck with Fortran storage association rules and seem to be less broken. All of these options should be used as a means of aiding the porting from one precision to another; not as some cheap, quick, and dirty solution for a port. – Steve Aug 21 '18 at 17:50
  • A big point of integer width tis that wgen calling any C libraries you must use the correct one epending what kind of int (long int, long long int) C uses. – Vladimir F Героям слава Aug 22 '18 at 05:16

1 Answers1

0

This is the minimum needed to make a call to METIS from Fortran:

program Test_Metis                                                              
use iso_c_binding                                                               
implicit none                                                                   
integer              :: nvtxs  = 15,       &  ! number of vertices              
                        nedgs  = 22,       &  ! number of edges                 
                        ncons  =  1,       &  ! number of constraints           
                        nparts =  2,       &  ! requested number of partitions  
                        objval,            &  ! return value from METIS call    
                        mopts(41)             ! array of options                
type(c_ptr)          :: vwgt  =c_null_ptr, &  ! weights of the vertices         
                        vsize =c_null_ptr, &  ! size of the vertices            
                        adjwgt=c_null_ptr     ! weights of the edges            
real                 :: ubvec                 !                                 
integer, allocatable :: xadj  (:),         &  ! variabes for ...                
                        adjncy(:),         &  ! ... compressed row storage         
                        part  (:)             ! partition of the grid           
real, allocatable    :: tpwgts(:)             ! desired weight for each partition

allocate(xadj  (nvtxs+1))                                                       
allocate(adjncy(nedgs*2))                                                       
allocate(part  (nvtxs))                                                         
allocate(tpwgts(nparts))                                                        

xadj = (/ 0,  2,  5,  8, 11, 13, 16, 20,  &                                     
         24, 28, 31, 33, 36, 39, 42, 44/)                                       
adjncy=(/ 1,  5,  0,  2,  6,  1,  3,  7,  2,  4,  8,  &                         
          3,  9,  0,  6, 10,  1,  5,  7, 11,  2,  6,  &                         
          8, 12,  3,  7,  9, 13,  4,  8, 14,  5, 11,  &                         
          6, 10, 12,  7, 11, 13,  8, 12, 14,  9, 13/)                           

tpwgts(:) =  1.0 / real(nparts)  ! all parts weigh the same                     
ubvec     =  1.001               ! this would be default by METIS anyway        
mopts(:)  = -1                   ! -1 is default value for all options          

call METIS_PartGraphRecursive(nvtxs,     &  ! (in), int                         
                              ncons,     &  ! (in), int                         
                              xadj,      &  ! (in), int(:)                      
                              adjncy,    &  ! (in), int(:)                      
                              vwgt,      &  ! (in), int(:)                      
                              vsize,     &  ! (in), int(:)                      
                              adjwgt,    &  ! (in), int(:)                      
                              nparts,    &  ! (in), int(:)                      
                              tpwgts,    &  ! (in), real(:)                     
                              ubvec,     &  ! (in), real(:)                     
                              mopts,     &  ! (in), int(:)                      
                              objval,    &  ! (out) int(:)                      
                              part)         ! (out) int(:)                      
end program                                                                     
Bojan Niceno
  • 113
  • 1
  • 1
  • 11
  • So where was the problem? What is the main point of this answer? There is something missing, you are passing the null pointers by reference. – Vladimir F Героям слава Aug 22 '18 at 05:11
  • @VladimirF: I think there were two problems, at least. First thing I was using Fortran's null() for null pointer, which is not quite the same thing as C pointer. Then I introduced iso_c_binding module, and used native C pointer with type(c_ptr) and setting them to c_null_ptr. That resolved the issue for most arguments passed, except the "ubvec" and "tpwgts". These don't accept pointers, no matter what METIS API documentation says. I don't know better than this. – Bojan Niceno Aug 22 '18 at 11:54
  • @Steve thanks for advice :-) – Bojan Niceno Aug 22 '18 at 11:56
  • But I don't see any any interface for `METIS_PartGraphRecursive` here. Do you have one? Anyway I am almost inclined to close this question as a duplicate of the one linked by @francescalus. But really do note that presently you are not passing a C pointer, but the *address of a C pointer*. You are not passing the null pointers by value but by reference. – Vladimir F Героям слава Aug 22 '18 at 12:24
  • @VladimirF : you might have a point; the question I raised is not much different the on pointed out by francescalus. Maybe is not a bad idea to close it. – Bojan Niceno Aug 25 '18 at 07:30