
Modern Fortran: Meta-programming with Fypp Bálint Aradi 2018 Workshop on Fortran Modernization for Scientific Application (ForMoSA) This work is licensed under a Creative Commons Attribution 4.0 International License. Outline Going beyond Fortran ● Meta-programming with the Fypp pre-processor 2 Why is modern Fortran not enough? Fortran language still misses some important capabilities ● Conditional compilation ● Generic programming techniques (a.k.a. templates) 3 Conditional compilation Parts of the code should be compiled only under certain conditions ● Optional external libraries (e.g. mathematical libraries ...) Lapack is not present during build Lapack is present during build subroutine solve(aa, bb) subroutine solve(aa, bb) ! Hand coded algorithm ! Invoke LAPACK routine … call dgesv(aa, bb, …) end subroutine solve end subroutine solve ● Different build types (e.g. debug vs. production, serial vs. MPI-parallel) Debug build (include consistency checks) Discard expensive checks subroutine solve(aa, bb) subroutine solve(aa, bb) ! Make consistency checks ! Do actual algorithm … … ! Do actual algorithm … 4 Alternative to conditional compilation ● You handle optional components on the Fortran level ● Define stub functions / data types if optional component is not present ● Link those functions / modules to your application, so that routine names can be resolved by the linker (although they should be never called) subroutine solve(aa, bb) subroutine dgesv(aa, bb, …) if (hasLapack) then write(error_unit, *) & call dgesv(aa, bb, …) & 'Internal error:& & dgesv called despite& else & missing LAPACK' ! Do hand coded way stop Stub function end subroutine solve used if built end subroutine solve without LAPACK Disadvantages: ● Mistakes in code-flow detected during run-time ● Tedious if optional component provides many routines ● Tedious if optional component provides complex data types (evtl. with type bound procedures) 5 Conditional compilation tools CoCo (Conditional Compilation) ● It was part of the Fortran standard, but has been officially withdrawn ● Open source (GPL) implementation (in Fortran!) exists ● Uses Fortran-like preprocessor language with ?? as prefix ?? logical, parameter :: has_intrinsic_module = .false. program hello_world ?? if (has_intrinsic_module) then use, intrinsic :: iso_fortran_env, only: output_unit write (unit=output_unit, fmt= *) 'Hello, world!' ?? else write (unit= *, fmt= *) 'Hello, world!' ?? endif end program hello_world Original example on Dan Nagle’s Technical Site 6 Conditional compilation tools C-preprocessor: cpp ● Using (abusing) the pre-processor of the C / C++ languages ● It may misinterprete language constructs if not used carfully (e.g. character concatenation operator // is a comment in C++) ● Output may not be 100% Fortran compatible (e.g. line length) Fortranized C-preprocessor: fpp ● Same syntax as cpp but (mostly) without C/C++ artifacts ● Bundled (in some form) with most compilers (GNU, Intel, NAG, …) ● Used by most many Fortran projects (either fpp or raw cpp) #ifdef WITH_EXTERNAL_LIBRARY call external_library_function() #else call hand_code_version() #endif 7 Conditional compilation tools Various open-source Fortran pre-processors ● f90ppr, Forpedo, Fpx3, PyF95++, PreForM.py, Fypp, ufpp, … ● See the Preprocessor page of the Fortran Wiki for some details ● Implemented in various languages, mostly Fortran and Python ● Their functionality and syntax differ (some of them cpp compatible, though) ● Level of documentation and support vary on a very broad scale Q Which pre-processor should I use for my Modern Fortran project? A1 None, just stick to the Fortran standard (the safe bet) A2 If you need conditional compilation only, take fpp as it is used by the majority of the Fortran projects (principle of least surprise) A3 At some point, you may need meta-programming capabilities, so let’s investigate further … 8 Meta-programming Fortran “enriched / degraded” with additional language constructs ● Code is not valid Fortran any more ● Pre-processor must be invoked before the compilation in order to transform the code into standard conforming Fortran code ● Strictly speaking: conditional compilation is alredy meta-programming ● Other languages (e.g. C++) provide standardized built-int tools (e.g. C++ templates) Q Do I really need this meta-mambo-jumbo for my Fortran project? A1 No! Already Fortran 77 offered all features a scientist / engineer ever needs. Everything since then just made Fortran more complicated and error prone without gaining anything... A2 Yes, you do! Let’s investigate a trivial example (proof of concept) 9 A trivial meta-programming task Swap the content of two variables of the same type, rank and shape integer, rank 0 version real, rank 0 version subroutine swap(aa, bb) subroutine swap(aa, bb) integer, intent(inout)& real, intent(inout)& & :: aa, bb & :: aa, bb integer, allocatable :: cc real, allocatable :: cc cc = aa cc = aa aa = bb aa = bb bb = cc bb = cc end subroutine swap end subroutine swap Any striking similarities between the two versions? ● Algorithms exactly the same, only data type changes 10 A trivial meta-programming task ● Exactly the same algorithm also works for multi-dimensional arrays (Fortran rocks!) real, rank 2 version subroutine swap(aa, bb) real, intent(inout) :: aa(:,:), bb(:,:) real, allocatable :: cc(:,:) cc = aa aa = bb bb = cc end subroutine swap ● Again only data type (and rank) changed 11 A trivial meta-programming task ● Abitrary derived types (or any arrays thereof) should be no problem either derived type, rank 0 version subroutine swap(aa, bb) type(MyType), intent(inout) :: aa, bb type(MyType), allocatable :: cc cc = aa aa = bb bb = cc end subroutine swap ● And so on and so on … ● It would be nice, if we had not to repeat the same code again and again (avoiding code duplication) 12 Meta-programming in Fortran Why not just repeat the code with all it types it is needed for? ● Error-prone → Are you sure you fixed the recent bug in all 27 duplicates in your code? ● Painfully complicated to share with others (when coding open source it’s all about sharing & colaborating) In order to use our generic library in your code, you have to carry out the following steps: 1. copy the interesting parts of the library into your code 2. find and replace all relevant types (marked by special markers) with the type you want to use it for. You will have to repeat the procedure for every type you need and every time you download an updated version of the library. We wish you good luck and a lot of fun! The Code Repeaters 13 Meta-programming in Fortran The (standard conforming!) trick with include ● Generic routines act on an undefined type with given name ● In the specific implementation the type with the given name is defined first, then the generic routines are included. generic.inc specific.f90 subroutine swap(aa, bb) type :: MyType type(MyType), … :: aa, bb … type(MyType), … :: cc end type MyType … … end subroutine swap include 'generic.inc' … Some disadvantages: ● The generic file is not self-contained (can not be compiled on its own) ● Only works with derived types, not with intrinsic types (integer, real, …) ● Generic file must contain a separate implementation for each rank 14 Meta-programming in Fortran Using a pre-processor ● Define a macro which contains the generic implementation with the type, rank, etc. as variables ● In the specific implementation expand the macro with the specific type name, rank, etc. ● Pass also all extra information needed to create the specific implementation via macro arguments (or pre-processor) variables Which preprocessor ? ● Theoretically most pre-processors (even fpp / cpp) can be used ● I think, Fypp can solve it more elegant and compact than the others ● Especially the code still looks quite pretty and clean after it had been pre-processed (easy to debug the specific implementation) NOTE: Do not believe me! Being the main author of Fypp, I may be biased a lot. Try the examples and the exercises with both, Fypp and your favorite pre-processor and judge yourself. 15 The Fypp-preprocessor General design principles ● Simple, easy to use pre-processor ● Emphasis on robustness and neat integration into Fortran developing toolchains and workflows. ● Avoid to create yet another mini-language for expression evaluation → Use Python expressions ● Make it easy to incorporate it in your project → One file only (just bundle this file with your project to avoid having Fypp as prerequisite) ● Break compatibility with the C-preprocessor (no reason to replicate all issues one gets when using cpp / fpp with Fortran), but look nevertheless similar to enable a quick learning curve. ● Minimalistic on features → It should not tempt you to do something with Fypp which can be done in Fortran with reasonable efforts 16 Fypp – quick feature overview Definition, evaluation and removal of variables The directive name is followed by a Python- expression Control-directives #:if DEBUG > 0 start with # print *, "Some debug information" : indicates that the #:endif entire line is part of the directive #:set LOGLEVEL = 2 print *, "LOGLEVEL: ${LOGLEVEL}$" #:del LOGLEVEL Expression evaluation { } mark begin and end special character is directives start with $ of in-line directives repeated at the end of in-line directives Expression is evaluated in Python and result is substituted in-place 17 Fypp – quick feature overview Macro definitions and macro calls ● Defines a parameterized text block, which can be inserted #:def ASSERT(COND) #:if DEBUG >
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages29 Page
-
File Size-