Automated Fortran–C++ Bindings for Scientific Applications

Automated Fortran–C++ Bindings for Scientific Applications

Automated Fortran–C++ Bindings for Scientific Applications Seth R Johnson High Performance Computing Methods & Applications Team Reactor and Nuclear Systems Division Oak Ridge National Laboratory ORNL is managed by UT–Battelle, LLC for the US Department of Energy Project collaborators: Matt Bement, Kate Evans, Tim Fuller, Andrey Prokopenko github.com/swig-fortran Motivation • C++ library developers: expand user base, more opportunities for development and follow-on funding • Fortran scientific app developers: use newly exposed algorithms and tools for your code • Multiphysics project integration: in-memory coupling of C++ physics code to Fortran physics code • Transitioning application teams: bite-size migration from Fortran to C++ !2 Background • Exascale Computing Project: many petascale scientific app codes still are primarily Fortran • Numerical/scientific libraries are primarily C/C++ • Expose Trilinos solver library to Fortran app developers: ForTrilinos product !3 Supported SWIG: Simplified Wrapper and Interface Generator Languages • Allegro CL • C# • CFFI • CLISP • Chicken • Generate interfaces to existing C and C++ code and data • D • Go types so that a target language can invoke functions and • Guile use the data • Java • Javascript • “Glue” code: flat C-linkage wrappers to C++ functions, • Lua • Modula-3 corresponding interfaces in target language • Mzscheme • OCAML • Does not couple target languages to other target languages • Octave • Perl • PHP • Does not parse target languages or create C++ proxy • Python wrappers • R • Ruby • Scilab • Tcl • UFFI !4 SWIG execution sequence • SWIG reads .i interface file which may %include other C/C++ headers to parse them • Interface file can be as little as a couple of lines for simple interfaces • More difficult C++/Fortran conversions require more interface code • SWIG generates source code files: .cxx and .f90 • This wrapper code can be distributed like regular source files • Library users don’t need to know SWIG User-written code SWIG-generated code Foo.cxx Foo.hh SWIG foo.f90 main.f90 Foo.i foo_wrap.cxx !5 Control flow and data conversion Call “native” procedure user.f90 Public module procedure Convert Fortran types to ISO_C_BINDING types module.f90 Call BIND(C) interface • Fortran 2003 standard defines C-compatible datatypes • Use only Fortran-compatible ISO C datatypes extern "C" wrapper function Convert C type to C++ type • Minimize data movement of numerical arrays Call C++ library function module_wrap.cxx C++ Library code library.cxx !6 Features • Primitive types • Function overloading • Enumerations • Template instantiation • Classes (with inheritance) • Compile-time constants • C strings and std::string • Exception handling • Function pointers • OpenACC and Thrust • Arrays and std::vector !7 Simple addition function: input and generated C code F/C interface #ifndef simplest_h SWIGEXPORT int _wrap_add( #define simplest_h int const *farg1, int add(int a, int b); #endif int const *farg2) { int fresult ; Input argument int arg1 ; conversion simplest.h int arg2 ; int result; arg1 = (int)(*farg1); arg2 = (int)(*farg2); %module simplest Wrapped result = (int)add(arg1,arg2); function call %include "simplest.h" fresult = (int)(result); return fresult; } simplest.i Output simplest_wrap.c argument conversion !8 Simple addition function: generated Fortran Fortran proxy function module simplest F/C .... use, intrinsic :: ISO_C_BINDING implicit none interface function add(a, b) & private result(swig_result) public :: add integer(C_INT) :: swig_result interface integer(C_INT), intent(in) :: a function swigc_add(farg1, farg2) & integer(C_INT), intent(in) :: b bind(C, name="_wrap_add") & integer(C_INT) :: fresult Input result(fresult) integer(C_INT) :: farg1 argument integer(C_INT), intent(in) :: farg1 integer(C_INT) :: farg2 integer(C_INT), intent(in) :: farg2 conversion integer(C_INT) :: fresult farg1 = a Wrapper end function farg2 = b function call end interface fresult = swigc_add(farg1, farg2) contains Output swig_result = fresult argument end function .... conversion end module simplest.f90 !9 Simple addition function: the part app devs care about SWIGEXPORT int swigc_add(int const *farg1, program main int const *farg2); use simplest, only : add write (0,*) add(10,20) end program simplest_wrap.c module simplest use, intrinsic :: ISO_C_BINDING main.f90 … contains function add(a, b) & result(swigf_result) integer(C_INT) :: swigf_result integer(C_INT), intent(in) :: a integer(C_INT), intent(in) :: b … end function $ ./main.exe end module simplest.f90 30 !10 Templated class Insert raw C++ template<typename T> %module "templated" Tell SWIG to class Thing { code into parse the T val_; generated %{ header file public: wrapper file #include "templated.hpp" Thing(T val); %} T get() const; %include "templated.hpp" }; // Instantiate templated classes template<typename T> %template(Thing_Int) Thing<int>; void print_thing(const Thing<T>& t); %template(Thing_Dbl) Thing<double>; // Instantiate and overload a function Instantiate %template(print_thing) print_thing<int>; templates %template(print_thing) print_thing<double>; at SWIG time templated.i !11 Templated class: generated Fortran wrapper code Memory module templated ownership interface print_thing … module procedure swigf_print_thing__SWIG_1, integer, parameter, public :: swig_cmem_own_bit = 0 swigf_print_thing__SWIG_2 integer, parameter, public :: swig_cmem_rvalue_bit = 1 end interface integer, parameter, public :: swig_cmem_const_bit = 2 public :: print_thing type, bind(C) :: SwigClassWrapper interface Overloaded type(C_PTR), public :: cptr = C_NULL_PTR subroutine swigc_delete_Thing_Int(farg1) & integer(C_INT), public :: cmemflags = 0 bind(C, name="_wrap_delete_Thing_Int") function end type ! class Thing< int > Opaque class … type, public :: Thing_Int wrapper contains type(SwigClassWrapper), public :: swigdata subroutine swigf_release_Thing_Int(self) Call delete if contains use, intrinsic :: ISO_C_BINDING we “own” procedure :: get => swigf_Thing_Int_get class(Thing_Int), intent(inout) :: self procedure :: release => swigf_release_Thing_Int type(SwigClassWrapper) :: farg1 the memory procedure, private :: swigf_Thing_Int_op_assign__ farg1 = self%swigdata generic :: assignment(=) => swigf_Thing_Int_op_assign__ end type Thing_Int if (btest(farg1%cmemflags, swig_cmem_own_bit)) then interface Thing_Int call swigc_delete_Thing_Int(farg1) module procedure swigf_create_Thing_Int Fortran endif end interface proxy class farg1%cptr = C_NULL_PTR ! class Thing< double > Second farg1%cmemflags = 0 type, public :: Thing_Dbl self%swigdata = farg1 … template end subroutine end type Thing_Dbl … instantiation … end module templated.f90 (2/2) !12 Exception handling %module except program main %include <std_except.i> use except, only : do_it, do_it_again, ierr, get_serr %exception { call do_it(-3) if (ierr /= 0) then SWIG_check_unhandled_exception(); write(0,*) "Got error ", ierr, ": ", get_serr() try { Replaced with ierr = 0 $action endif } wrapper call call do_it(2) catch (const std::exception& e) { call do_it(-2) SWIG_exception(SWIG_RuntimeError, e.what()); call do_it_again(321) } end program } main.f90 %{ #include <stdexcept> Got error -3 : In do_it(int): #include <iostream> %} NOOOOO %inline { Yes! I got 2 void do_it(int i) { terminate called after throwing an if (i < 0) throw std::logic_error("NOOOOO"); instance of 'std::runtime_error' std::cout << "Yes! I got " << i what(): An unhandled exception << std::endl; occurred before a call to } void do_it_again(int i) { do_it(i); } do_it_again(int); in do_it(int): NOOOOO } !13 Array views subroutine sort(data) use, intrinsic :: ISO_C_BINDING integer(C_INT), dimension(:), target :: data integer(C_INT), pointer :: farg1_view %module algorithm type(SwigArrayWrapper) :: farg1 Treat as native if (size(data) > 0) then %{ Fortran array farg1_view => data(1) #include <algorithm> farg1%data = c_loc(farg1_view) %} farg1%size = size(data) else %include <typemaps.i> farg1%data = c_null_ptr %apply (SWIGTYPE *DATA, size_t SIZE) { farg1%size = 0 (int* data, std::size_t size) }; end if call swigc_sort(farg1) %inline { end subroutine void sort(int* data, std::size_t size) { program main std::sort(data, data + size); use, intrinsic :: ISO_C_BINDING } use algorithm, only : sort } implicit none integer(C_INT), dimension(6) & :: test_int = (/ 3, -1, 7, 3, 1, 5 /) call sort(test_int) write (*,*) test_int end program !14 Thrust + OpenACC %module thrustacc; program test_thrustacc use thrustacc %include <openacc.i> implicit none %include <thrust.i> integer, parameter :: n = 32768 integer :: i real, dimension(:), allocatable :: a %{ #include <thrust/sort.h> ! Generate N uniform numbers on [0,1) %} allocate(a(n)) %inline %{ call random_number(a) template<typename T> void swig_thrust_sort(thrust::device_ptr<T> !$acc data copy(a) DATA, size_t SIZE) { !$acc kernels do i = 1,n thrust::sort(DATA, DATA + SIZE); a(i) = a(i) * 10 + i } end do %} !$acc end kernels call sort(a) %template(sort) swig_thrust_sort<int>; !$acc end data %template(sort) swig_thrust_sort<float>; write(*,*) sum(a) %template(sort) swig_thrust_sort<double>; end program !15 Automatic BIND(C) wrapping %module bindc module bindc Only generate … interface code type, bind(C), public :: Point %fortranbindc; real(C_FLOAT), public :: x real(C_FLOAT), public :: y %fortran_struct(Point) Don’t create real(C_FLOAT), public :: z end type Point proxy class … %inline { interface typedef struct { float x, y, z; } Point; subroutine print_point(p) & bind(C, name="print_point")

View Full Text

Details

  • File Type
    pdf
  • Upload Time
    -
  • Content Languages
    English
  • Upload User
    Anonymous/Not logged-in
  • File Pages
    20 Page
  • File Size
    -

Download

Channel Download Status
Express Download Enable

Copyright

We respect the copyrights and intellectual property rights of all users. All uploaded documents are either original works of the uploader or authorized works of the rightful owners.

  • Not to be reproduced or distributed without explicit permission.
  • Not used for commercial purposes outside of approved use cases.
  • Not used to infringe on the rights of the original creators.
  • If you believe any content infringes your copyright, please contact us immediately.

Support

For help with questions, suggestions, or problems, please contact us