Skip to content

sbrass/fortran-workshop

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Fortran-Workshop

1 Begrüßung und Vorbereitung

1.1 Tutoren

  • Simon Braß, Doktorand in der AG Theoretische Teilchenphysik
  • Jessica Reuter, Lehramt für Mathematik und Informatik

1.1.1 Fragen? Probleme?

./images/complex_conjugate.png

1.2 Ablauf

1.2.1

1.2.1.1 1. Tag: Grundlagen

  • Vorbereitung
  • Geschichte
  • Konzepte und Syntax
  • Strukturen
  • Datentypen
  • Ein-/Ausgabe

1.2.2

1.2.2.1 2. Tag: OOP

  • derived types
  • Zeiger und Prozeduren-Zeiger
  • allocatables
  • Polymorphie und Vererbung
  • Typ-gebundene Prozeduren

1.2.3 Uhrzeit und Ort

  • EN-D308, 4.12.2012, 16:00 - 20:00 Uhr
  • EN-D308, 5.12.2012, 16:00 - 20:00 Uhr

1.3 Vorbereitung

1.3.1 Editor

  • Emacs
  • Vim / vi / kak
  • Sublime
  • Atom

1.3.2 Kompiler

  • nagfor
  • ifort
  • gfortran

1.3.3 Anleitungen und Material

Anleitungen und Material unter: http://www.tp.nt.uni-siegen.de/+brass/

2 Geschichte und Einführung

2.1 Geschichte von Fortran

2.1.1 Formula Translator (Fortran)

1950er
Entwicklung bei IBM um das Team von John Backus als Assembler-Alternative
1966
1. Standardisierung durch ANSI als FORTAN 66
1978
2. Standardiserung als FORTRAN 77 / Durchbruch als Sprache
1991
Einführung von abstrakten Datentypen und data hiding, sowie Standardisierung der array language
1997
Fortran 95 als high perfomance Sprache
2004
Fortran 2003 mit Objekt-orientierten Konzepten
2010
Fortran 2008 mit submodules und coarrays
2012-heute
Fortran 2018 …

2.2 Philosophie von Fortran

  • einfach-lesbare und verständliche Syntax
  • vergleichbare Effizienz zu Assembler-Code
  • Mathematik-nahe Umsetzung FORmula TRANnslator
  • fixed form Syntax für Lochkarten-Computer in FORTAN 66/77
  • free form seit Fortran 90 als Standard

./images/punchcard.jpg

2.3 Beispiele

2.3.1 fixed form

c     independent variables for real graph: number of final state
c     legs times 3, take away 4 for 4-momentum conservation, add 2
c     for x_1 and x_2, and take away an overall azimuth
      real * 8 btilde,xx(ndiminteg),www0
      real * 8 xrad(3)
      real * 8 xborn(ndiminteg-3)

2.3.2 free form

call self%rand_gen%rand(randkoord)
randkoord = self%sys_len * randkoord
m = int(randkoord(1)) + 1
n = int(randkoord(2)) + 1
call cyclic_coord (self, m, n, up, down, left, right)

deltaE = 2.0_dp * ( self%couple_const * self&
     &%grid(m, n)*(self%grid(m, left) + self%grid(m, right) +&
     & self%grid(up, n) + self%grid(down, n)) + self%mag_field &
     &* self%grid(m, n))

2.4 Fortran 2003 Standard

2.4.1 Neuerungen in Fortran 2003

  • parametrized derived types, Konstruktoren, Destruktoren und eingeschränkte Zugriffe
  • Polymorphie, Typ-Erweiterung, Vererbung, dynamische Typ-Allokierung und Typ-gebundene Prozeduren
  • allokierbare Komponenten, deferred type parameters
  • Prozeduren-Zeiger
  • C bindings

2.4.2 Gfortran

  • (fast) vollständige Unterstützung durch Gfortran 7.2.0 (GCC Wiki)
  • größte Kompiler-Unterstützung an sich (ifort/NAGfor/Gfortran)

2.5 Parallelisierung

2.5.1 Threading

  • Parallelisierung mit OpenMP durch Direktiven mit Threads standardisiert

2.5.2 Message-Passing Interface

  • Parallelisierung im Message-Passing Interface standardisiert

3 Konzepte und Syntax

3.1 Warum Fortran?

3.1.1 Vorteile von Fortran

  • gewöhnliche Sprache
  • Syntax nahe an Matrix- und Vektorproblemen
  • strenge Speicherverwaltung
  • Polymorphie mit Typ-Sicherheit
  • by-reference Argumentübergabe

3.1.2 Nachteile von Fortran

  • schlechte Kompiler-Unterstützung
  • keine Templates (C++)
  • langsame Zeiger

3.2 Aufteilung und Verschachtelung

3.2.1 Scope

Definition eines Scopes durch Strukturen

  • Hauptprogramm
  • Module
  • Prozeduren
  • derived types

3.2.2 data hiding

  • Verschachtelung von Scopes
  • Zugriff auf Daten eines niedrigeren Scopes
  • kein Zugriff auf Daten eines höhren Scopes
  • explizites Öffnen / Verschließen von Daten

3.3 Beispiel Syntax und Konzepte

3.3.1 Hauptprogramm

3.3.1.1 Hauptprogramm

program beispiel
  ! use beispiel_module
  implicit none
  ! Variablen-Deklaration
  ! Programm
contains
  ! Prozeduren
  ! Zugriff auf alle Variablen
  ! des Hauptprogrammes
end program beispiel

3.3.2 Modul

3.3.2.1 Modul

module beispiel_module
  use beispiel2_module
  ! Variablen-Deklarationen
  ! Typ-Definitioen
  !
contains
  ! Module-Prozeduren
  ! Zugriff auf alle Variablen
  ! des Moduls
end module beispiel_module

3.3.3 Beipsiel

  • Kein Zugriff von beispiel_module auf program
  • Zugriff von contained procedure in program auf program

3.4 Besondere Zeichen

3.4.1 Kommentare

Kommentare im Quellcode können mit ! erstellt werden. Jeder Ausdruck nach ! wird vom Kompiler ignoriert.

3.4.2 Zeilenumbruch

Überlange Zeilen (>=127 Zeichen) werden mit & gebrochen.

3.4.3 Statement-Ende

Mit einem ; kann ein Statement-Ende gesetzt werden so, dass mehrere Statements in eine Zeile geschrieben werden können.
! Ich bin ein Kommenatar.
real :: x = 1.5 ! Ich bin auch ein Kommentar.
character(len=100) :: str = "Hallo Welt! Ich bin eine viel zu lange &
     & Zeichenkette, denn ich passe nicht in eine Zeile."
i = 42; b = 10; c = (0.5, 4.5)

3.5 Datentypen

3.5.1 Intrinsiche Datentypen

integer :: i                  ! Ganze Zahlen
real :: x                     ! Reelle Zahlen
complex :: z                  ! Komplexe Zahlen (5., 2.): 5+2i
character(len=100) :: str     ! Zeichenkette mit Länge 100
logical :: flag               ! Boolean (.True. oder .False.)
type(haus) :: eigenheim       ! Derived Type

3.5.2 implicit none

Wichtig: Aus Rückwartskompatibilität immer implicit none benutzen, damit Fortran nicht automatisch Variablen mit entsprechenden Typen deklariert.

3.5.3 Deklaration

Variablen können nur am Anfang (des Programmes/Modules/Prozedur) deklariert werden, sobald eine andere Anweisung kommt, sind Deklarationen verboten.

3.6 Ein- und Ausgabe

3.6.1 Listen-formatierte Ein-/Ausgabe

print *, "Hallo Welt!" ! Schreibt "Hallo Welt!" in die Standardausgabe
write (*, *) "Hallo Welt!"
write (*, *) "Geben Sie einen ganze Zahl ein:"
read (*, *) i ! Liest einen Wert von der Standardeingabe ein
write (u, *) "Ich werde in die unit = ", u, " geschrieben."

3.6.2 iso_fortran_env

  • andere unit als * (Standardein-/ausgabe) möglich
  • Standardausgabe-, Standardfehlerausgabe-/unit/ in iso_fortran_env
use iso_fortran_env, only: error_unit
! Schreibt Fehlermeldung auf stderr
write (error_unit, *) "Fehlermeldung"

3.7 Kompilieren

  • statisch-kompilierte Sprache
  • Kompilieren zu Machinen-Code
    gfortran \
        -Wall \ # alle Warnungen
    -g \    # Debug-Symbole
    -c example1.f08 -o example1.o
        
  • Linken zu einem ausführbaren Programm
    gfortran -o example1 example1.o
        

3.7.1 Programm: Beispiel 1

program example1
  implicit none
  integer :: i
  print *, "*** Beispiel 1 ***"
  print *, "Hallo Welt!"
  i = 42
  print *, "i = ", i
end program example1

3.7.2 Ausgabe

gfortran -o example.o \
         -c example.f08
gfortran -o example example.o
./example
*** Beispiel 1 ***
Hallo Welt!
i =           42

3.8 Aufgabe

  • Schreiben Sie das Programm: Beispiel 1 ab und kompilieren Sie es.
  • Was ist der Unterschied zwischen example.o und example.f08?
  • Was geschieht beim sog. Linken mit example.o?

3.8.1 objdump

Benutzen Sie objdump -d example1.o, um sich den Machinen-Code anzusehen. Versuchen Sie das Beispiel ohne die print-Anweisungen.

3.9 Variablen-Eigenschaften

Alle Variablen können weitere Eigenschaften besitzen:

  • bei parameter kann sich die Variable nicht mehr ändern,
  • durch dimension wird eine Variable zu einem Variablen-Array,
  • durch allocatable kann der Speicher dynamisch angefordert werden,
  • durch pointer kann mit einer Variable auf eine andere Variable mit target gezeigt werden.

3.9.1 Achtung

Eigenschaften können sich gegenseitig ausschließen!

integer, pointer, target :: i1 ! Verboten
integer, allocatable, pointer :: i2 ! Verboten
integer, parameter, pointer :: i3 ! Verboten
! ...

3.10 Operatoren und mathematische Funktionen

print *, "a + b = ", a + b      ! Addition
print *, "a - b = ", a - b      ! Subtraktion
print *, "a * b = ", a * b      ! Multiplikation
print *, "a / b = ", a / b      ! Division
print *, "a ** b = ", a ** b    ! Potenz
  • alle mathematischen Operatoren und Funktionen für komplexe Zahlen
  • Vielzahl von mathematischen Standardfunktionen: mod, sin, log,…
  • logische Operatoren: .and., .or., .not.,…
  • vergleichende Operatoren: ===, /=, >, <, >=, <=
print *, ".true. .and. .false.", .true. .and. .false.
print *, ".true. .or. .true.", .true. .or. .true.
print *, "(a - b) /= (a + b)", (a - b) /= (a + b)

3.11 Aufgabe

  • Deklarieren Sie mit allen Datentypen Variablen.
  • Definieren Sie für alle Variablen Werte.
  • Geben Sie alle Variablen auf der Standardausgabe aus.
  • Wenden Sie (ausgewählte) mathematische Operatoren auf Variablen gleichen und verschieden Typs an.

3.11.1 Grundgerüst

program aufgabe1
  implicit none
  integer :: zahl
  ! ...
  zahl = 42
  ! ...
  print *, "zahl = ", zahl
  ! ...
  print *, "zahl + 1. = ", zahl + 1.
end program aufgabe1

3.12 Arrays

  • von allen Datentypen (auch derived types)
  • maximal 7-dimensionaler Array
  • 1-dm. Konstruktor durch [...]
  • allokierbarer Array mit allocatable
integer, dimension(20) :: iarray
integer, dimension(10, 2) :: iarray2
integer, dimension(:), allocatable :: iarray3
! Maximal 7-dim. Array
iarray = 10                     ! alle Element auf 10 setzen
iarray = [(i,i=1, 20)]           ! Konstruktor
iarray = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
iarray2(:, 1) = [(i, i = 1, 10)]
iarray2(:, 2) = [(i**2, i = 1, 10)]
iarray3 = [1, 2]                ! Allocate on assignment
deallocate (iarray3)
allocate (iarray3(10), source = 1)

3.12.1 Array-Konstruktor

  • 1-dim. Array kann mit [...] initialisiert werden
  • mehr-dim. Array kann mit der reshape-Funktion initialisiert werden
  • reshape (1D-ARRAY, SHAPE...) mit einem eindim.-Array von der Größe von array und einem SHAPE-Array

3.12.1.1 Mehr-dim. Konstruktor

! Fülle iarray2 mit iarray2 auf
iarray2 = reshape (iarray, [10, 2])

3.12.2 Index

  • Zugriff auf Elemente eines Array durch Index
  • Einzel-Element oder Unter-Array
  • Zugriff auf slice durch :
  • Index-Variablen vom Typ integer
  • Index läuft von 1...N
integer, dimension(10, 3) :: 2d_array
! ...
print *, 2d_array(5, 2) ! Element
print *, 2d_array(1:5, 1) ! Unterarray
print *, 2d_array(5, :) ! Slice im höchsten Index
print *, 2d_array(2 * i, i) ! i = {1, 2, 3}

3.12.3 Array-Speicher

  • Spalten-orientierter Array
  • Anordnung von Array-Speicher nach niedrigster zur höchster Dimension
  • Effizienz bei Schleifen über mehr-dim. Arrays

3.12.4 Schlechtes Beispiel

integer, dimension(100, 25) :: a
integer :: i, j
do i = 1, 100 ! linker, innerer Index
   do j = 1, 25 ! rechter, äußerer Index
      print *, a(i, j)
   end do
end do

Die Schleife über den Index j läuft über Spalten und der Index i über die Reihen. Daher muss im obigen Beispiel sehr viel im Speicher hin und her gesprungen werden.

3.12.5 Gutes Beispiel

integer, dimension(100, 25) :: a
integer :: i, j
do j = 1, 25 ! rechter, äußerer Index
   do i = 1, 100 ! linker, innerer Index
      print *, a(i, j)
   end do
end do

Der Speicher wird in der vorgegebenen Reihenfolge abgelaufen so, dass der Array effizient ausgelesen wird. Im Beispiel macht das natürlich keinen Unterschied wegen des print auf die Standardausgabe!

3.12.6 Spezielle Funktionen

  • size(ARRAY, DIM...) gibt die Größe des Arrays zurück, mit DIM kann eine Dimension ausgewählt werden
  • lbound(ARRAY, DIM...) gibt alle kleinsten Index des Arrays (in der jeweiligen Dimension)
  • ubound(ARRAY, DIM...) gibt alle größten Index des Arrays (in der jeweiligen Dimension)
integer, dimension(10, 5, 20) :: array
print *, "size(array) = ", size (array)
print *, "size(array, dim=1) = ", size (array, dim=1)
print *, "lbound(array) = ", lbound(array)
print *, "ubound(array, dim=2)", ubound(array, dim=2)
print *, "lbound(array, dim=3) = ", lbound(array, dim=3)

3.13 if-clause und Schleifen

  • if-clause prüft logischen Ausdruck
  • logischen Ausdruck aus logischen Operatoren und Variablen
  • select case
  • do / do while / cycle / exit
if (a == b) then ! /= für ungleich
   print *, "a == b"
else if (a >= b)  then ! >, <, <=
   print *, "a >= b"
else
   print *, "a steht in keiner Relation zu b"
end if
do i = 1, 10 ! Drittes Argument gibt Iterationsvorschrift
   print *, "i = ", i
end do
do i = 10, 1, -1
   print *, "i = ", i
end do
do while (a > b)
   b = b + 2
   print *, "b = b + 2 = ", b
end do
  • Schleifen können ein (eindeutiges) Label haben
  • Label kann wichtig sein für cycle oder exit
  • Zugriff auf Schleife mit Label durch cycle/exit label
infty: do ! "Endlos-Schleife"
   ! Tue etwas
   if (bedingung1) then
      cycle infty ! Wiederhole Schleife sofort
   else if (bedingung2) then
      exit infty ! Beende Schleife sofort
   else
      ! Tue etwas ganz anderes
   end if
end do infty
! select case als Ersatz für verschachtelte if-clauses
select case (test)
case(10)
   print *, 10
case(:9)
   print *, "<10"
case default
   print *, "Was auch immer..."
end select
! elementweise Zuordnung
forall (i = 1:10)
   array(i) = array(i) + 1
end forall
! elementweise Bearbeitung über Bedingung
where (array == 1)
   array = array + 1
else where
   array = 0
end where

3.14 Aufgabe

  • Deklarieren Sie eindimensionale Arrays von verschiedenen Datentypen.
  • Testen Sie welche mathematischen Operatoren erlaubt sind:
    • Skalar und Array
    • Array und Array
  • Deklarieren, (de)allokieren Sie Arrays.
  • Nutzen Sie eine Schleife um einen Array elementeweise auszugeben.

3.14.1 Hinweis

  • Testen Sie, ob ein allocatable Objekt allokiert ist, mit allocated(...).
  • Probieren Sie ein allocate-on-assignment aus.
  • Greifen Sie auf ein Element mit Index i mit iarray(i) zu.

3.15 Funktionen

Eine Funktion liefert einen Rückgabe-Wert aus verschiedenen Argumenten. Ähnlich einer Abbildung aus der Mathematik.

! Funktion vom Typ integer
integer function add (a, b) result (c)
  integer, intent(in) :: a
  integer, intent(in) :: b
  c = a + b
end function add
! ...
print *, "2 + 3 = ", add(2, 3)
  • pure Funktionen sind ohne Nebeneffekte wie Ein-/Ausgabe
  • elemental Funktionen werden elementweise auf Arrays angewant, setzt pure voraus.
  • Bei Definition ohne result wird automatisch eine Rückgabe-Variable mit dem Namen und des Types der Funktion angelegt.

3.16 Aufgabe

  • Schreiben Sie eine ganzzahlige Funktion, welche einen 1-dim. Array vom Typ integer nimmt und die Summe der Elemente berechnet.
  • Schreiben Sie eine reelle Funktion, welche einen 1-dim. Array vom Typ real nimmt und das Produkt aller Elemente bildet.
  • Schreiben Sie eine logische Funktion, welche nur .true. liefert, wenn kein Element eines integer Array null ist.
  • Schreiben Sie eine reelle Funktion, welche einen 2-dim. Array vom Typ real nimmt und die Elemente der ersten Dimension durch Elemente der zweiten Dimension teilt.
  • Schreiben Sie eine reelle Funktion, welche das Skalarprodukt zwischen zwei gleich-großen Vektoren bestimmt.

3.16.1 Hinweis

  • Mit der Funktion size können Sie die Größe eines Arrays bestimmen.
  • Lösung

3.17 Subroutinen

Subroutinen können mehrere Argumente zurückgeben.

subroutine volume_and_length (a, b, volume, circ)
  real, dimension(3), intent(in) :: a
  real, dimension(3), intent(in) :: b
  real, intent(out) :: volume
  real, intent(out) :: circ
  real, dimension(3) :: c
  call vector_product (a, b, c)
  volume = sqrt(dot_product(c, c))
  circ = 2*(sqrt(dot_product(a, a)) + sqrt(dot_product(b, b)))
contains
  subroutine vector_product (a, b, c)
    real, dimension(3), intent(in) :: a, b
    real, dimension(3), intent(out) :: c
    c = [(a(1) * b(2) - a(2) * b(1)), &
         (a(1) * b(3) - a(3) * b(1)), &
         (a(2) * b(3) - a(3) * b(2))]
  end subroutine vector_product
end subroutine volume_and_length
  • Funktionen und Subroutinen können andere Prozeduren über contains enthalten.
  • intent-Angabe geben Veränderlichkeit von Argumenten an: in, out, inout
  • optional-Argumente können, müssen aber nicht übergeben werden.
character(:), allocatable, optional :: text
if (present (text)) print *, text
  • Übergabe der Argumente in Reihenfolge
  • Übergabe der Argumente über Name des Arguments: a=array, b=array2
  • intent(in/inout)-Arrays können assumed-shape haben
subroutine map_index (index1, index2)
  integer, dimension(:), intent(in) :: index1
  integer, dimension(size(index)), intent(out) :: index2
end subroutine map_index
! im Programm
integer, dimension(5) :: i1, i2
call map_index (i1, i2)

3.17.1 Problem

Wie bringt man Struktur und logische Ordnung in die Prozeduren und das Hauptprogram?

3.18 Modul

  • Strukturierung und logische Ordnung
  • Modularisierung
  • Zugriffsrechte
  • Schnittstellen und Definitionen
  • Wiederverwendbarkeit
module vektoren
  implicit none
  private ! Immer alles auf private setzen
  public :: vector3_product ! explizit exportieren
contains
  subroutine vector3_product (a, b, c)
    real, dimension(3), intent(in) :: a, b
    real, dimension(3), intent(out) :: c
    c = [(a(1) * b(2) - a(2) * b(1)), &
         (a(1) * b(3) - a(3) * b(1)), &
         (a(2) * b(3) - a(3) * b(2))]
  end subroutine vector3_product
end module vektoren
  • Einbindung von Modulen durch use
  • Beschränkung durch only
  • Umbenennung von geladenen Prozeduren neu => alt
  • private Elemente nicht nach außen sichtbar
  • public Elemente nach außen sichtbar
program main
  use vektoren, only: vector_product => vector3_product
  ! Nur vector3_product importieren und umbenennen
  ! ...
  call vector_product (a, b, c)
  print *, "a x b = ", c
end program main

3.19 Zeichenketten

  • Zeichenketten sind quasi 1-dim. Arrays vom Typ character
  • Spezielle Syntax character(:)
  • Zugriff auf Teile einer Zeichenkette durch Indizes
  • Zeichenketten zusammenfügen mit //
    character(len=100) :: str
    character(:), allocatable :: str2, str3 ! allocatable-string
    str = "Hallo Welt!"
    print *, str
    str2 = "Hallo Welt! Ich bin neuer!!"
    print *, str2
    str3  = "Und meine Geschichte ist es..."
    str2 = str2 // " " // str3           ! String konkatieren mit //
    print *, str2
        

3.20 Sonstiges

Was nicht behandelt wurde:

  • Datentyp-Genauigkeit über kind
  • Vertiefung von Ein- und Ausgabe
  • Umgang mit Dateien
  • return in Prozeduren
  • Rekursion
  • interfaces / Operator-=interfaces=
  • Prozeduren als Argumente

3.21 Wiederholung 1. Tag

  • Intrinsische Datentypen
  • Bedingungen und Schleifen
  • Funktionen und Subroutinen
  • Module

3.21.1 Ziel 2. Tag

Objekt-orientierte Liste mit beliebig erweiterbaren Listen-Elementen und Strukturierung auf verschiedene Module und Dateien.

4 Objekt-orientiertes Programmieren

4.1 Zeiger

  • Zeiger entspricht einer Speicher-Adresse einer anderen Variable
  • Ziel-Objekt muss vom Typ target sein
  • Zeiger-Sicherheit: Programmabbruch bei Zugriff auf undefinierten Zeiger
real, dimension(:, :), pointer :: p_array ! undefiniert
real, dimension(10, 10), target :: array1, array2
array1 = 1.
array2 = 2.
! Zuweisung Zeiger auf Ziel
p_array => array1
if (.not. associated (p_array)) then ! Zeiger ist nicht zugewiesen
   p_array => array2
end if
print *, p_array(:, 1)
nullify (p_array)               ! Zurücksetzen
p_array => array2
print *, p_array(:, 1)
nullify (p_array)
if (.not. associated (p_array)) then
   print *, p_array
end if

4.2 interfaces

Ein interface stellt dem Kompiler aller Informationen zur Prozedur-Art, sowie Argumente usw. zur Verfügung.

  • Implizites interface automatisch durch Deklaration einer Prozedur
  • Explizite Definition eines interfaces für Prozeduren, Operatoren oder Konstruktoren
  • Überladen eines interfaces mit einer weiteren Definition generic interface
interface
   logical function non_zero (a) return (yorn)
     integer, intent(a) :: a
   end function non_zero
end interface
  • Operator-=interface= durch Modul-Prozedur
  • Funktion mit zwei intent(in)-Argumenten
  • Assignment-=interface= durch Modul-Prozedur
  • Subroutinen mit erstem Argument mit intent(out) oder intent(inout) und zweitem Argument intent(in)
module math
  implicit none
  interface operator(*)
     module procedure matrix_vector_mult
  end interface operator(*)
contains
  function matrix_vector_mult (A, x) result (y)
    integer, dimension(:, :), intent(in) :: A
    integer, dimension(:), intent(in) :: x
    integer, dimension (size (x)) :: y
    integer :: i
    do i = 1, size (x)
       y(i) = sum(A(:, i)) * x(i)
    end do
  end function matrix_vector_mult
end module math
  • Prozeduren als Argument
  • Argument durch interface
real function integrate (func, a, b) result (value)
  interface
     real function func (x)
       real, intent(in) :: x
     end function func
  end interface
  real, intent(in) :: a, b
  ! Integrieren mit Simpson-Regel
end function integrate

4.3 derived types

  • eigene Typ-Definition aus intrinsischen Typen und derived types
  • derived type kann allocatables und pointer beinhalten
  • vordefinierter Konstruktor
  • Typ-Name per Konvention mit _t
  • private / public (allgemein oder elementweise)
! Typ Definition
type :: haus_t
   private
   real, public :: length
   real, public :: width
   real, public :: height
end type haus_t
type(haus_t) :: eigenheim
! Konstruktor
eigenheim = haus_t (10.0, 8.0, 12.0) ! Länge, Breite und Höhe
eigenheim = haus_t ( &               ! Beliebige Reihenfolge
     width = 8.0, &                  ! mit explizitem Aufruf
     height = 12.0, &                ! der Argumente
     length = 10.0)
! Oder eigener Konstruktor durch ein überladenes Interface von haus_t
print *, "eigenheim%height = ", eigenheim%height ! Zugriff auf Element
  • Erweiterung eines derived type durch Vererbung
  • Überschreiben aller Eigenschaften möglich
  • Zugriff auf alle Elemente durch variable%element
  • Zugriff auf ursprüngliche Elemente des Basis-Typ durch new_type%basic_type%element
type, extends(haus_t) :: halle_t
   character(len=20) :: fassadenfarbe = "grün"
   logical :: brandschutz = .false.
end type halle_t
type(halle_t) :: gewerbe ! hat alle Eigenschaften von haus_t und hallo_t
gewerbe = halle_t ( &
     width = 8.0, height = 12.0, length = 20., &
     fassadenfarbe = "rot", brandschutz = .true.)
print *, gewerbe%haus_t%width, gewerbe%width
  • abstrakter Typ als Basis-Typ ohne direkte Verwendung
  • abstrakter Typ muss erweitert werden
type, abstract :: grundstueck_t
   logical :: bebaut = .false.
end type grundstueck_t
type, extends(grundstueck_t) :: bau_grundstueck_t
   real :: length, height, width
   logical :: construction
end type bau_grundstueck_t
type, extends(grundstueck_t) :: wald_t
   real :: anteil_baeume
end type wald_t
  • polymorphe Objekte/=class(…)=-Objekte
  • polymorpe Objekte erben alle Eigenschaften, bzw. können alle Eigenschaften überladen
  • Typ-Sicherheit durch select type
class(grundstueck_t), allocatable :: grundstueck
allocate (wald_t :: grundstueck)
select type (grundstueck)
type is (wald_t)
   grundstueck%length = 100
   grundstueck%height = 15
   grundstueck%width = 250
   grundstueck%anteil_baeume = 0.8
   ! type is (bau_grundstueck_t)
   ! class is (...)
   ! class default
end select
  • Typ-gebundene Prozeduren
  • 1. Argument ist pass-Objekt durch class(...)
  • explizite Angabe des pass-Objekt mit pass(...)
  • nopass, pass, private/public, deferred
  • Überladen von Prozeduren kann verhindert werden durch non_overridable
  • deferred-Prozeduren nur bei abstract types
  • generic
  • final
type :: func_t
 contains
   procedure, pass(object), public :: write => func_write
   procedure, private :: quadrature => func_quadrature
   procedure, private :: simpson => func_simpson
   generic, public :: integrate => simpsons, quadrature
   final :: func_finalize
end type func_t

4.4 Aufgabe

Implementieren Sie eine Objekt-orientierte Liste mit beliebig erweiterbaren Listen-Elementen. Nehmen Sie folgende Punkte als Hilfestellung:

  1. Schreiben Sie ein Modul list.
  2. Das Modul definiert einen abstrakten Datentyp basic_list_element_t für ein beliebiges Listen-Element, welches nur einen Zeiger auf das nächste Element enthält und einen Index-=integer=.
  3. Der abstrakte Typ basic_list_element_t soll eine deferred Typ-gebundene Prozedur write besitzen. Geben Sie ein abstract interface vor.
  4. Das Modul definiert einen Typen basic_list_t, welcher einen Zeiger auf das erste Elemente und das nächste (letzte) Element besitzt.
  5. Der Typ basic_list_t soll mehrere Typ-gebundene Prozeduren besitzen, eine add-Prozedur, welche ein Element nimmt und zur Liste anfügt, sowie eine write-Prozedur.
  6. Der Typ basic_list_t soll eine final-Prozedur besitzen, welche die Liste elementweise deallokiert.
  7. Schreiben Sie ein Modul list_integer und erweiteren Sie darin, den abstrakten Typ basic_list_element_t, so dass Sie einen integer-Wert im Element speichern können.
  8. Implementieren Sie im Modul list_integer die deferred Prozedur write von basic_list_element_t.
  9. Schreiben Sie ein Testprogramm, welches die Liste mit mehreren integer-Werten füllt und geben Sie sich diese aus.

4.5 Modul list

  • abstrakter Datentyp basic_list_element_t mit Zeiger auf das nächste Element und dem Index des Elements
  • Datentyp basic_list_t als Verwalter der Listen-Struktur mit add, write und final
  • abstract interface für deferred Typ-gebundene Prozedur basic_list_element_write, welche später vom erweiternden Typen implementiert werden muss.
module list
  type, abstract :: basic_list_element_t
     class(basic_list_element_t), pointer :: next => null ()
     integer :: index = -1
   contains
     procedure(basic_list_element_write), deferred, public :: write
  end type basic_list_element_t
  type :: basic_list_t
     private
     class(basic_list_element_t), pointer :: first => null (), last => null ()
   contains
     procedure, public :: add => basic_list_add
     procedure, public :: write => basic_list_write
     final :: basic_list_final
  end type basic_list_t
  abstract interface
     subroutine basic_list_element_write (self, unit)
       import :: basic_list_element_t
       class(basic_list_element_t), intent(in) :: self
       integer, intent(in) :: unit
     end subroutine basic_list_element_write
  end interface
contains
  <<module_list_contains>>
end module list
  • basic_list_add nimmt ein polymorphes Objekt vom Typ basic_list_element_t und fügt an das Ende der Liste an und setzt den Index des Elementes.
  • basic_list_write schreibt den Inhalt der Liste auf die Standardausgabe.
  • basic_list_final implementiert das Deallokieren der Listen-Elemente und wird automatisch aufgerufen, wenn das Listen-Objekt zerstört wird.
subroutine basic_list_add (self, element)
  class(basic_list_t), intent(inout) :: self
  class(basic_list_element_t), intent(inout), target :: element
  if (.not. associated (self%first)) then
     self%first => element
     self%last => element
     element%index = 1
  else
     self%last%next => element
     element%index = self%last%index + 1
     self%last => element
  end if
end subroutine basic_list_add
subroutine basic_list_write (self, unit)
  class(basic_list_t), intent(in) :: self
  integer, intent(in) :: unit
  class(basic_list_element_t), pointer :: element
  print *, "*** basic_list_write ***"
  element => self%first
  do while (associated (element))
     call element%write (unit)
     element => element%next
  end do
end subroutine basic_list_write
subroutine basic_list_final (self)
  type(basic_list_t), intent(inout) :: self
  class(basic_list_element_t), pointer :: element
  do while (associated (self%first))
     element => self%first
     self%first => self%first%next
     deallocate (element)
  end do
end subroutine basic_list_final

4.6 Modul list_int

  • Der Typ list_int erweitert den abstrakten Datentyp so, dass das Element ein integer-Wert beinhaltet.
  • Das interface list_element_int_t überlädt das Standard-=interface= des Typ-Konstruktor mit einer eigenen Funktion.
module list_int
  use list, only: basic_list_element_t
  implicit none
  private
  type, extends (basic_list_element_t) :: list_element_int_t
     private
     integer :: value = 0
   contains
     procedure, public :: write => list_element_int_write
  end type list_element_int_t
  interface list_element_int_t
     module procedure list_element_int_init
  end interface list_element_int_t
  public :: list_element_int_t
contains
  <<module_list_int_contains>>
end module list_int
  • Die Funktion list_element_int_init wird über das interface zum Konstruktor des Datentyps.
  • Die Prozedure list_element_int_write implementiert die deferred Typ-gebundene Prozedur.
type(list_element_int_t) function list_element_int_init (value) result (self)
  integer, intent(in) :: value
  self%value = value
end function list_element_int_init
subroutine list_element_int_write (self, unit)
  class(list_element_int_t), intent(in) :: self
  integer, intent(in) :: unit
  print *, "value(", self%index, ") = ", self%value
end subroutine list_element_int_write

4.7 Testprogramm

  • Die Liste wird mit jeweils neu-allokierten Elementen gefüllt und im Anschluss wird die Liste auf der Standardausgabe ausgegeben.
program example
  use list
  use list_int
  implicit none
  type(basic_list_t) :: l
  integer :: i
  do i = 1, 10
     call add_int_element (i * (i - 1))
  end do
  call l%write (0)
contains
  subroutine add_int_element (value)
    integer, intent(in) :: value
    class(basic_list_element_t), pointer :: element
    allocate (list_element_int_t :: element)
    select type (element)
    type is (list_element_int_t)
       element = list_element_int_t (value)
    end select
    call l%add (element)
  end subroutine add_int_element
end program example

5 Beispiele

5.1 Programm: Example 1

<<example1>>

5.2 Programm: Example 2

program example2
  implicit none
  integer :: i
  <<example2>>
  print *, "*** Example 2 ***"
  print *, "iarray = ", iarray
  print *, "iarray2 = ", iarray2
  print *, "iarray3 = ", iarray3
end program example2

5.3 Programm: Example 3

program example3
  complex :: a
  complex :: b
  print *, "*** Example 3 ***"
  a = (2, 1)
  b = (-1, 2)
  print *, "a = ", a
  print *, "b = ", b
  <<example3>>
end program example3

5.4 Programm: if-clause und Schleifen

program example46
  implicit none
  integer :: a = 5, b = -1
  integer :: i
  print *, "*** Beispiel 4-6 ***"
  print *, "a = ", a
  print *, "b = ", b
  <<example_conditionals>>
  <<example_loops>>
end program example46

5.5 Programm: Example 7

program example7
  implicit none
  real, dimension(3) :: a, b
  real :: vol, circ
  print *, "*** Beispiel 7 ***"
  a = [1., 2., 3.]; b = [-1., 2., 1.]
  call volume_and_length (a, b, vol, circ)
  print *, "a = ", a, "b = ", b
  print *, "vol = ", vol, "circ = ", circ
end program example7
<<example7>>

5.6 Programm: Example 8

program example8
  implicit none
  <<example8>>
end program example8

5.7 Programm: Example 9

program example9
  implicit none
  <<example9>>
end program example9

5.8 Programm: Example 10

program example10
  implicit none
  <<example10>>
end program example10

5.9 Programm: Array Funktionen

program example
  implicit none
  <<example_array_functions>>
end program example

5.10 Programm: Operator

<<example_operator>>
program example
  use math
  implicit none
  integer, dimension(3, 2) :: A
  integer, dimension(2) :: x
  A = reshape ([1, 5, 4, 2, 3, 4], shape=[3, 2])
  x = [10, -3]
  print *, "A.x = ", A * x
end program example

6 Lösung

6.1 Aufgabe: Funktionen

program solution1
  implicit none
  integer :: i
  integer, dimension(10) :: i_vector
  real, dimension(5) :: r_vector
  print *, "*** Lösung 1 ***"
  i_vector = [(i, i = 1, 10)]
  r_vector = [(0.2 * i, i = 1, 5)]
  print *, "sum_vector: ", sum_vector (i_vector)
  print *, "product_vector: ", product_vector (r_vector)
  print *, "non_zero_element: ", non_zero_element (i_vector - 1)
contains
  <<solution1>>
  <<solution2>>
  <<solution3>>
end program solution1
integer function sum_vector (vector) result (sum)
  integer, dimension(:), intent(in) :: vector
  integer :: i, n_vector
  n_vector = size (vector)
  sum = 0
  do i = 1, n_vector
     sum = sum + vector(i)
  end do
end function sum_vector
real function product_vector (vector) result (product)
  real, dimension(:), intent(in) :: vector
  integer :: i, n_vector
  n_vector = size (vector)
  product = 1
  do i = 1, n_vector
     product = product * vector(i)
  end do
end function product_vector
logical function non_zero_element (vector) result (yorn)
  integer, dimension(:), intent(in) :: vector
  yorn = .not. any (vector == 0) ! all (vector /= 0)
end function non_zero_element

6.2 Aufgabe: Liste

<<module_list>>
<<module_list_int>>
<<test_list>>

About

No description or website provided.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published