FORTRAN: 1. FORTRAN

HANDLEIDING FORTRAN

HOOFDSTUK 1: FORTRAN


1.9 Subprogramma's

We zullen twee soorten subprogramma's bekijken: subroutines en functions. Een subroutine wordt aangeroepen met het CALL statement en heeft de volgende vorm:
        SUBROUTINE naam ( parameterlijst )
        declaraties
        statements
        END
De statements kunnen executeerbare en comment statements zijn, maar ook CALL statements en functiereferenties. Een subprogramma wordt afgesloten door het woord END. Zoals we al gezien hebben geeft END tijdens executie een return naar het aanroepende programmma.

Voorbeeld:

Stel we hebben drie subroutines, genaamd SUB1, SUB2, SUB3 die we afhankelijk van de input willen uitvoeren. Het hoofdprogramma functioneert dan als een ``monitor''. Het volgende is een kompleet, maar triviaal, FORTRAN programma.

     10 CONTINUE
        WRITE (6, *) ' GEEF EEN INTEGER WAARDE TUSSEN 1 EN 3: '
        READ (5, *, END=20) IPROG
        IF (IPROG .EQ. 1) THEN
           CALL SUB1
           GO TO 10
        ELSE IF (IPROG .EQ. 2) THEN
           CALL SUB2
           GO TO 10
        ELSE IF (IPROG .EQ. 3) THEN
           CALL SUB3
           GO TO 10
        ELSE
           WRITE (6, *) ' DEZE WAARDE IS FOUT: ', IPROG
           GO TO 10
        ENDIF
     20 END

        SUBROUTINE SUB1
        WRITE (6, *) ' ENTRY SUB1 '
        END

        SUBROUTINE SUB2
        WRITE (6, *) ' ENTRY SUB2 '
        END

        SUBROUTINE SUB3
        WRITE (6, *) ' ENTRY SUB3 '
        END
In dit voorbeeld treffen we de eenvoudigste vorm van subroutines, nl. subroutines zonder parameters. We kunnen een subroutine een parameterlijst meegeven, zoals in het volgende voorbeeld:
        DIMENSION A(100)

        WRITE (6, *) ' ENTER N <= 100'
        READ (5, *) N

        DO 10 I=1,N
           A(I) = REAL(I)
     10 CONTINUE

        CALL SUM (A, N, S)
        WRITE (6, 600) N,S
    600 FORMAT (' SUM OF FIRST ', I3, ' NUMBERS OF A IS ', F5.0)
        END

        SUBROUTINE SUM (A, N, S)

  C     A EN N ZIJN INPUT PARAMETERS, S IS EEN OUTPUT PARAMETER.

        DIMENSION A(100)

        S = 0.
        DO 10 I=1,N
           S = S + A(I)
     10 CONTINUE
        END
In dit voorbeeld krijgt de subroutine twee waarden via de parameters A en N en geeft er één weer terug via parameter S. In zo'n geval (één waarde terug) is een FUNCTION vaak handig te gebruiken.
Hetzelfde programma als boven, maar dan in dubbele precisie en met een function reference, i.p.v. een subroutine call, ziet er als volgt uit:
        DOUBLE PRECISION A(100), SUM

        WRITE (6, *) ' ENTER N <= 100'
        READ (5, *) N

        DO 10 I=1,N
           A(I) = DBLE(I)
     10 CONTINUE

        S = SUM (A, N)
        WRITE (6, 600) N, S
    600 FORMAT(' SUM OF FIRST ', I3, ' NUMBERS OF A IS ', F5.0)
        END

        DOUBLE PRECISION FUNCTION SUM (A, N)

  C     A EN N ZIJN INPUT PARAMETERS, FUNCTIEWAARDE SUM IS HET RESULTAAT

        DOUBLE PRECISION A(100)

        SUM = 0.D0
        DO 10 I=1,N
           SUM = SUM + A(I)
     10 CONTINUE
        END
Merk op dat de naam van de functie (in dit voorbeeld SUM) een waarde krijgt binnen de functie (aan de linkerkant van een assignment statement staat). Dit is de (enige) manier om een waarde toe te kennen aan een functie.

Een functiereferentie kan in het aanroepende programma deel uit maken van expressies. Voorbeelden van referenties naar de bovenstaande functie SUM:

        S = - SUM(A,N)
        IF ( ABS(SUM(A,N)) .LT. 1.D-7 ) GO TO 100
        S2 = SUM(A,N)**2
        T  = 1.D0/SUM(A,N) + B + SQRT(SUM(A,N))
In het geval van een functie moet de functiewaarde van hetzelfde type zijn in het refererende (sub)programma als in de functie. Het type van een FUNCTION wordt als volgt gedefinieerd:
        type FUNCTION naam (arg1, arg2,  )
Dus bijvoorbeeld:
        DOUBLE PRECISION FUNCTION INTEGR (F, X1, X2)
        LOGICAL FUNCTION TEST (I)
        CHARACTER*10 FUNCTION VOORN (NAAM)
        INTEGER FUNCTION INDEX (A)
        REAL FUNCTION VALUE
Als het type van de functie niet op deze manier gedefinieerd wordt, wordt de default (integer: I-N, real: A-H, O-Z) aangenomen. Denk erom dat het type van de functie ook in het aanroepende programma gedeklareerd moet worden, tenzij de naam ervan met de default klopt!

Functies en subroutines worden onafhankelijk gecompileerd, d.w.z. dat de compiler zich niets herinnert van de compilatie nadat het END statement gepasseerd is. Dit houdt in dat elke variabele die niet het default type heeft in elke functie en subroutine opnieuw gedeklareerd moet worden. Ook zal dezelfde variabele naam in verschillende subprogramma's een verschillende geheugenlocatie aanduiden.

Alle variabelen zijn lokaal, tenzij zij argument van de functie of de subroutine zijn. Als een variabele doorgegeven wordt als een argument, moet het type in aanroepende en aangeroepen programma kloppen. Dit is de verantwoordelijkheid van de programmeur, alweer omdat de compiler niet over de grenzen van subprogramma's heen kan kijken. De naam van een argument hoeft echter niet hetzelfde te zijn.

Voorbeeld:

        CHARACTER*1 A
        LOGICAL BIT

        BIT = .TRUE.
        A = 'A'
        CALL COMPUT (A)
  C     HIER HEEFT DE LOGICAL BIT NOG STEEDS DE WAARDE .TRUE.
        END

        SUBROUTINE COMPUT (ALFA)
  C     ALFA ADRESSEERT DEZELFDE GEHEUGENLOCATIE ALS A IN HOOFDPROGRAMMA

  C     DE CHARACTER VARIABELE BIT IN DEZE SUBROUTINE HEEFT NIETS
  C     TE MAKEN MET DE LOGICAL VARIABELE BIT IN HOOFDPROGRAMMA

        CHARACTER*1 BIT, ALFA

  C     HIER KRIJGT BIT DE WAARDE 'A' (DE WAARDE VAN ALFA):
        BIT = ALFA
        END
Het grote voordeel van functies en subroutines is dat men een FORTRAN programma kan stuctureren. Men kan de subprogramma's onafhankelijk schrijven en testen en ze zo klein houden dat ze overzichtelijk blijven. Men kan ook subprogramma's aanroepen die geen deel van het eigen FORTRAN programma uitmaken, maar ergens in gecompileerde vorm op schijf staan.

Voorbeelden hiervan zijn de functies die bij FORTRAN horen. De belangrijkste FORTRAN functies zijn:

      LOG      natuurlijke logarithme
      EXP      e-macht
      ASIN     arcsinus
      ACOS     arccosinus
      ATAN     arctangens
      SIN      sinus
      COS      cosinus
      TAN      tangens
      ABS      absolute waarde
      SQRT     vierkantswortel
      REAL     maak real van integer
      DBLE     maak double precision van integer of real
      MAX      maximum van twee of meer getallen
      MIN      minimum van twee of meer getallen

Deze functienamen zijn z.g. ``generic'' namen, d.w.z. dat zij de naam zijn van een aantal verwante functies, namelijk functies die van elkaar verschillen in hun type. De compiler bepaalt uit de context welke functie precies bedoeld wordt en roept die aan. Tot nu toe hebben we steeds voorbeelden gezien waarbij we via END terugkeerden, dus in het laatste statement van het subprogramma. We kunnen echter ook eerder terugkeren, met behulp van het executeerbare statement RETURN. Stel de onderstaande subroutine TASK heeft drie verschillende taken, en heeft als input parameter de integer ITASK, die de gewenste taak specificeert. TASK heeft de volgende schematische vorm:

        SUBROUTINE TASK (ITASK)
        IMPLICIT DOUBLE PRECISION (A-H, O-Z)

        IF (ITASK .EQ. 1 ) THEN
  C        perform task1
           RETURN

        ELSE IF (ITASK .EQ. 2 ) THEN
  C        perform task2
           RETURN

        ELSE IF (ITASK .EQ. 3 ) THEN
  C        perform task3
           RETURN

        ELSE
           WRITE (6, *) ' ILLEGAL ITASK, EXECUTION STOPS'
           STOP
        ENDIF
        END
Dit voorbeeld laat ook zien dat we executie overal kunnen beeindigen waar we willen met behulp van STOP. Dit is handig als een fout ontdekt wordt tijdens executie.

OPGAVEN:

  1. Schrijf een subroutine die de polaire coördinaten r, theta en phi ( de laatste twee in graden!) omrekent naar de corresponderende cartesische coördinaten.
            x = r cos phi sin theta
            y = r sin phi sin theta
            z = r cos theta
    
    Geef beide coördinaatstellen door als een array. Denk eraan dat de FORTRAN functies hoeken in radialen verwachten. Het getal pi kan je berekenen via: PI=ACOS(-1.D0).

  2. Schrijf een functie die de waarde van een genormeerde $p_x$ orbital in een punt $(x,y,z)$ geeft
             p_x = \sqrt{\alpha^5 \over \pi} x \exp^{-\alpha r}
    
    hier is $r$ de lengte van de vector $(x,y,z)$, en $\alpha$ de orbital exponent. De functie heeft als input parameters $\alpha$ en een array met $x$, $y$ en $z$.

  3. Schrijf een hoofdprogramma dat eerst een orbital exponent inleest. Zet de volgende opdrachten in een loop (einde invoer = einde loop):
    • Lees coördinaten van een punt en een woord dat specificeert of de coördinaten cartesisch of polair zijn.
    • Indien invoer polair converteer dan naar cartesisch m.b.v. de eerder geschreven subroutine.
    • Roep de px orbital functie aan.
    • Print de waarde, en lees de coördinaten van het volgende punt.


Updated 30-jan-1996, pfk