L9 Fortgeschrittene Konzepte generische Unterprogramme Viele vordefinierte Funktionen in Fortran lassen verschiedene Argumenttypen zu. SIN(arg) z.B. liefert ein Ergebnis vom Typ REAL, DOUBLE PRECISION, COMPLEX je nach Typ von arg. Fortran erlaubt es mehrere Unterprogramme unter einem generischen Namen zusammen zu fassen. PROGRAM generic ! This program demonstrates how ! to define a generic procedure for swapping reals, ! integers and logicals using external procedures. REAL INTEGER LOGICAL DOUBLE PRECISION :: :: :: :: a, b ia, ib la, lb da, db INTERFACE swap ! generic name SUBROUTINE swapreal(u,v) REAL, INTENT(INOUT) :: u, v END SUBROUTINE swapreal SUBROUTINE swapint(u,v) INTEGER, INTENT(INOUT) :: u, v END SUBROUTINE swapint SUBROUTINE swaplog(u,v) LOGICAL, INTENT(INOUT) :: u, v END SUBROUTINE swaplog END INTERFACE PRINT*,"swap reals.." a = 1.0; b = 2.0 PRINT*,"before: a,b=",a,b CALL swap(a,b) PRINT*,"after: a,b=",a,b PRINT*,"swap integers.." 1 ia = 1; ib = 2 PRINT*,"before: CALL swap(ia,ib) PRINT*,"after: ia,ib=",ia,ib ia,ib=",ia,ib PRINT*,"swap logicals.." la = .TRUE.; lb = .FALSE. PRINT*,"before: la,lb=",la,lb CALL swap(la,lb) PRINT*,"after: la,lb=",la,lb PRINT*,"swap double precision.." da = 1.D0; db=2.D0 PRINT*,"before: da,db=",da,db ! CALL swap(da,db) Compilation error: No specific match PRINT*,"after: da,db=",da,db END PROGRAM generic ! external subroutines follow SUBROUTINE swapreal(u,v) ! swap two reals REAL, INTENT(INOUT) :: u, v REAL :: temp temp = u; u = v; v = temp END SUBROUTINE swapreal SUBROUTINE swapint(u,v) ! swap two integers INTEGER, INTENT(INOUT) :: u, v INTEGER :: temp temp = u; u = v; v = temp END SUBROUTINE swapint SUBROUTINE swaplog(u,v) ! swap two logicals LOGICAL, INTENT(INOUT) :: u, v LOGICAL :: temp temp = u; u = v; v = temp END SUBROUTINE swaplog L9 Fortgeschrittene Konzepte Ein gutes Beispiel wäre eine Fourier Transformation. Man kann diese mit komplexen Zahlen oder reellen Zahlen formulieren. INTERFACE fft ! real or complex SUBROUTINE fft_real(data,coef) REAL, DIMENSION(:), INTENT(in) :: data REAL, DIMENSION(:), INTENT(out) :: coef END SUBROUTINE fft_real SUBROUTINE fft_complex(data,coef) COMPLEX, DIMENSION(:), INTENT(in) :: data COMPLEX, DIMENSION(:), INTENT(out) :: coef END SUBROUTINE fft_real END INTERFACE Viele brauchbare Forrtan Unterprogramme findet man unter www.netlib.org 2 L9 Fortgeschrittene Konzepte Unterprogramme als Parameter Im Folgenden ist für den Compiler nicht sofort ersichtlich, dass das Argument f ein FUNCTION Unterprogramm ist. Klarheit schafft der INTERFACE Block. PROGRAM DEMO .... REAL, EXTERNAL :: f, fderiv .... y=f(x+c) ! kein Feld sondern EXTERNAL .... CALL minimize(f,xa,xb,step,x1) ! f(x) is a FUNCTION CALL minimize(fderiv,xa,xb,step,x2) ! fderiv(x) is a FUNCTION ... SUBROUTINE minimize(f,a,b,step,x_minimum) ! minimum vom f(x) in (a,b) REAL, INTENT(in) :: a, b, step REAL, INTENT(out) :: x_minimum INTERFACE REAL FUNCTION f(x) RESULT (value) REAL, INTENT(in) :: x END FUNCTION F END INTERFACE start=f(a) ... END SUBROUTINE minimize 3 L9 Fortgeschrittene Konzepte Generische Bezeichner (hier: Benutzerdefinierte Operatoren) MODULE vectors TYPE :: vector REAL :: x REAL :: y REAL :: z END TYPE ! von mir selbstdefinierter Typ ! kann auch integer, logical, Felder ! enthalten INTERFACE OPERATOR (.cross.) ! .beliebigerName. ! die Namen .TRUE. und .FALSE. sind verboten MODULE PROCEDURE cross_product END INTERFACE INTERFACE OPERATOR (*) ! "Operator overload" ! erlaubt sind nur * + - < > dies kann durchaus verwirren MODULE PROCEDURE cross_product END INTERFACE INTERFACE ASSIGNMENT (=) ! erweitert die Bedeutung von = MODULE PROCEDURE vector2array ! muss subroutine sein MODULE PROCEDURE array2vector END INTERFACE CONTAINS FUNCTION cross_product(a,b) RESULT (c) ! TYPE (vector) :: c ! TYPE (vector), INTENT(in) :: a, b -> -> -> c = a x b c%x = a%y*b%z - a%z*b%y c%y = a%z*b%x - a%x*b%z c%z = a%x*b%y - a%y*b%x END FUNCTION cross_product 4 SUBROUTINE vector2array(array,vec) REAL, DIMENSION(3), INTENT(out) :: array TYPE(vector), INTENT(in) :: vec array(1)=vec%x; array(2)=vec%y; array(3)=vec%z END SUBROUTINE vector2array SUBROUTINE array2vector(vec,array) REAL, DIMENSION(3), INTENT(in) :: array TYPE(vector), INTENT(out) :: vec vec%x=array(1); vec%y=array(2); vec%z=array(3) END SUBROUTINE array2vector END MODULE vectors PROGRAM Test_vectors USE vectors TYPE(vector) :: i, j, k REAL, DIMENSION(3) :: a3 i%x=1; i%y=0; i%z=0 j%x=0; j%y=1; j%z=0 ! unit vectors ! unit vector x ! unit vector y k = i .cross. j ! cross_product PRINT*,"i .cross. j =",k k = i * j ! cross_product PRINT*,"i * j=",k%x,k%y,k%z PRINT*,"5*4=",5*4 ! geht wie gehabt a3 = i ! = : vetor2array PRINT*,"a3=",a3 j = a3 ! = : array2vector PRINT*,"j=",j%x,j%y,j%z END PROGRAM Test_vectors L9 Fortgeschrittene Konzepte Elemental function/subroutine Einfach strukturierte Unterprogramme, als PURE SUBROUTINES bezeichnet, die keine IF Abfragen enthalten können sowohl für einfache Größen als auch für Felder vom gleichen Typ benutzt werden, wenn sie als ELEMENTAL deklariert sind. ELEMENTAL SUBROUTINE dewpoint(T_D, r, p) ! non-SI units ! C g/kg mb ! calculate the dewpoint according to Bolton (1980) MWR IMPLICIT NONE REAL, INTENT(out) :: T_D REAL, INTENT(in) :: r REAL, INTENT(in) :: p REAL ! C ! g/kg ! mb :: esat esat=p*r/(622.+r) T_D=(243.5*LOG(esat)-440.8)/(19.48-LOG(esat)) ! (16) ! (11) END SUBROUTINE dewpoint Hier (wegen ELEMENTAL) ist eine explizite Schnittstelle erforderlich, also entweder ein INTERFACE oder das Unterprogramm wird in ein MODULE gepackt. Der Aufruf könnte dann etwa so aussehen: USE sounding_tools ! contains subroutine dewpoint REAL, DIMENSION(100) :: Tdew, rmix, pmb REAL :: Tp ... CALL dewpoint(Tdew(1:N),rmix(1:N),pmb(1:N)) ! arrays CALL dewpoint(Tp,12.2,965.0) 5 L9 Fortgeschrittene Konzepte Unterprogramme als (Dummy-)Argumente und optionale Argumente Wichtig: FUNCTION Unterpropgramme als EXTERNAL deklarieren. PROGRAM FindZero REAL, EXTERNAL :: f, fderiv ! f and fderiv are FUNCTION(s) ! (and NOT ARRAYS) !REAL, EXTERNAL :: g, gderiv INTERFACE ! this INTERFACE is needed only because some ! arguments are declared optional SUBROUTINE Newton(x0,f,df,tolerance,iterations_used) REAL, INTENT(inout) :: x0 REAL, INTENT(in), OPTIONAL :: tolerance INTEGER, INTENT(out), OPTIONAL :: iterations_used INTERFACE REAL FUNCTION f(x) RESULT (value) REAL, INTENT(in) :: x END FUNCTION f END INTERFACE INTERFACE REAL FUNCTION df(x) RESULT (value) REAL, INTENT(in) :: x END FUNCTION df END INTERFACE END SUBROUTINE NEWTON END INTERFACE PRINT*,"f(10.),fderv(10.)=",f(10.),fderiv(10.) r0=10. ! Startwert CALL Newton(x0=r0,f=f,df=fderiv,tolerance=1.E-3,iterations_used=niter) PRINT*,"r0=",r0," niter=",niter 6 ! jetzt kommt die naechste Funktion dran !CALL Newton(x0=r0,f=g,df=gderiv) END PROGRAM FindZero SUBROUTINE Newton(x0,f,df,tolerance,iterations_used) REAL, INTENT(inout) :: x0 REAL, INTENT(in), OPTIONAL :: tolerance INTEGER, INTENT(out), OPTIONAL :: iterations_used INTERFACE ! Argument f ist ein Unterprogramm REAL FUNCTION f(x) RESULT (value) REAL, INTENT(in) :: x END FUNCTION F END INTERFACE INTERFACE ! Argument df ist ein Unterprogramm REAL FUNCTION df(x) RESULT (value) REAL, INTENT(in) :: x END FUNCTION df END INTERFACE ! --- Ende Deklarationsteil IF(PRESENT(tolerance)) THEN converg=tolerance ELSE converg=EPSILON(xbeliebigesREAL) ! ca. 1.E-6 ENDIF DO iter=1,1000 ! iter ist fuer mich klarer als n ! (n+1) (n) (n) (n) x0 = x0 - f(x0)/df(x0) ! x = x - f(x )/f’(x ) IF(ABS(f(x0)) < converg) THEN IF(PRESENT(iterations_used)) iterations_used=iter RETURN ENDIF ENDDO Bronstein STOP "no convergence" END SUBROUTINE NEWTON ! external subroutines follow FUNCTION f(x) RESULT (value) REAL, INTENT(in) :: x value=1./x-x END FUNCTION f FUNCTION fderiv(x) RESULT (value) REAL, INTENT(in) :: x value=-1./x**2-1. END FUNCTION fderiv