generische Unterprogramme

Werbung
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
Herunterladen