Home | Sitemap  | Impressum | KIT
OpenFOAM

C++ basierte Open Source Library für die Analyse von Problemen der Struktur- und Strömungsmechanik

aktuelle Version: 2.4.0

Ansprechpartner: Dienste:
Links:
Lizenztyp:
Bereitstellung:

Einführung in OpenFOAM

OpenFOAM (Open Source Field Operation and Manipulation) ist eine Open Source CFD Toolbox, bestehend aus C++ Libraries und Executables oder auch als Applications bezeichnet. Eine genauere Beschreibung findet man in der Dokumentation.

Das Modell ist in einem sog. Case-Verzeichnis abgelegt, welches aus weiteren Unterverzeichnissen und Dateien besteht und in dem u.a. die Netz- und Modellbeschreibungen durch sog. Dictionaries festgelegt sind. DieNetze können sowohl direkt per Editor im blockMeshDict, dem Blockmesh Directory, eingegeben werden, es gibt aber auch eine Reihe von Konvertierungsprogrammen, die aus den Meshformaten anderer kommerzieller Programme OpenFOAM-Formate erzeugen können, so u.a.

  • aus Fluent .msh-Files
  • aus STAR-CD/ProStar Meshfiles
  • aus Gambit .neu-File

Die Prä/Postprozessoren HyperMesh und ANSYS ICEM_CFD selbst können direkt Netze für OpenFOAM erzeugen.

Der Standardpostprozessor für OpenFOAM ist Paraview. Aber es gibt auch Tools, mit denen die Ergebnisse in

  • ein Fluent-Format
  • ein Fieldview-Format
  • ein EnSight-Format

konvertiert werden können. Außerdem kann auch ein VTK-Format erzeugt werden, welches wiederum mit Paraview dargestellt werden kann.

Es gibt eine Menge an Standard Solvern für alle möglichen Strömungsmodelle, wie

  • inkompressibel, kompressibel
  • Multiphasenströmung
  • Direct Numerical Simulation (DNS)
  • Wärmeausbreitung und Auftriebsströmung
  • Teilchentransport
  • Molekulardynamische Methoden
  • Monte-Carlo-Methoden
  • Elektrostatik und magnetohydrodynmische Strömungen
  • Spannungs-Dehnungs-Analyse von Festkörpern
  • Black-Scholes-Gleichung im Finanzbereich

und viele Turbulenzmodelle, die hier nicht im Einzelnen aufgezählt werden sollen.

OpenFOAM ist im SCC auf dem bwUniCluster, ForHLR I und ForHLR II installiert. Die parallele Bearbeitung lässt sich sehr einfach durchführen, in dem das Netz partitioniert wird. Für jeden Prozessor wird ein eigenes Verzeichnis angelegt, in dem die Ergebnisse der bearbeiteten Domäne geschrieben werden. Anschließend werden die Ergebnisse aus den Partitionen wieder zusammengeführt.

Open Source Licensing

Die Nutzungsbedingungen von OpenFOAM finden Sie auf der WebSite von OpenFOAM Foundation:

http://www.openfoam.org/licence.php

Kurzanleitung

 

Einleitung 

Steuerung

Geometrie und Vernetzung

Anfangs- und Randbedinungen

Physikalische Eigenschaften

Diskretisierung und Solver

Start des Solvers

Postprozessing

Parallelverarbeitung

 

 

Einleitung

Am Beispiel eines kleines Problems, Lid-Driven Cavity Flow, also die Strömung in einem Kasten bei einer vorgegeben Geschwindigkeit an der Deckfläche, soll das Prinzip von OpenFOAM vorgestellt werden. In OpenFOAM wird grundsätzlich in dreidimensionaler Geometrie gerechnet. Wenn ein Problem als zweidimensionales Modell gerechnet werden soll, sollte eine Elementschicht als Dicke in die dritte Richtung erzeugt und an den Begrenzungsflächen entsprechende Randbedingungen gesetzt werden (s.u.)

Wíchtig: Das Beispiel beschreibt die Vorgehensweise am KIT-Cluster HC3.

Zunächst muss eine neue case Struktur angelegt werden.

wobei für case ein Name stehen kann, in dieser Demonstration z.B. cavity. Die Case-Struktur besteht aus drei Unterverzeichnissen

 

system enthält       

controlDict 

in dem Anfangs-/Endzeitpunkt und Zeitschritte bzw. Ein-/Ausgabeintervalle für die Ergebnisse festgelegt werden

  fvSchemes in dem die Differentialgleichungen formuliert werden
  fvSolution  in dem der Solver spezifiziert wird.
constant enthält polyMesh  in dem die Geometrie  und die Boundaries beschrieben sind
  Properties das sind Dictionaries in dem die physikalische Eigenschaften beschrieben werden
Time Directories 0, ... Das Verzeichnis 0 enthält die Anfangsbedingungen zur Startzeit (=0). Im controlDict wird als Zeitinkrement deltaT festgelegt und ein writeInterval, so dass alle deltaT*writeInterval Zeiteinheiten, die Ergebnisse in ein neues Verzeichnis herausgeschrieben werden.

 

Dazu kommen ggf. noch weitere Verzeichnisse und Dateien.

Steuerung

Zunächst wird das OpenFOAM Environment erzeugt. Dazu gibt man

. set_OF

ein.

Über die Steuerung wird die Start- und Endzeit und Zeitschrittweite, sowie das Ausgabeintervall für die Ergebnisse festgelegt. Dazu wird ein controlDict im Unterverzeichnis system mit dem folgenden Inhalt angelegt:

 

FoamFile
{
version     2.0;
format      ascii;
class       dictionary;
location    "system";
object      controlDict;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

application     icoFoam;
startFrom       startTime;
startTime       0;
stopAt          endTime;
endTime         0.5;
deltaT          0.005;
writeControl    timeStep;
writeInterval   20;
purgeWrite      0;
writeFormat     ascii;
writePrecision  6;
writeCompression uncompressed;
timeFormat      general;
timePrecision   6;
runTimeModifiable yes;

 

Die meisten Parameter sind selbsterklärend, ansonsten findet man eine genauere Beschreibung im OpenFOAM User's Guide.

Geometrie und Vernetzung

Die einfache Geometrie erlaubt es, ein strukturiertes Netz zu erzeugen. Dazu gibt es das blockMesh Tool. Bei komplizierteren Geometrien können unstrukturierte Netze mit anderen Präprozessoren wie z.B. HyperMesh oder ANSYS ICEM_CFD generiert werden.

Es wird ein blockMeshDict in das polyMesh Unterverzeichnis gelegt. Hierin ist die Geometrie und das Netz beschrieben, sowie auch die Randflächen.

 

FoamFile
{
version     2.0;
format      ascii;
class        dictionary;
object      blockMeshDict;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

convertToMeters 0.1;

vertices
(
(0 0 0)
(1 0 0)
(1 1 0)
(0 1 0)
(0 0 0.1)
(1 0 0.1)
(1 1 0.1)
(0 1 0.1)
);

blocks
(
hex (0 1 2 3 4 5 6 7) (20 20 1) simpleGrading (1 1 1)
);

edges
(
);

patches

(
wall movingWall
(
(3 7 6 2)
)
wall fixedWalls
(
(0 4 7 3)
(2 6 5 1)
(1 5 4 0)
)
empty frontAndBack
(
(0 3 2 1)
(4 5 6 7)
)
);

mergePatchPairs
(
);

Header

 

 

 

 

 

alle Längeneinheiten werden mit 0.1 multpliziert

 

 

Definitionen von Ränder für spätere Randbedingungen

 

Basierend hierauf wird das Netz erzeugt, in dem man

blockMesh -case <Pfad>/cavity

eingibt bzw. wenn man im cavity Unterverzeichnis ist, reicht die Eingabe von

blockMesh

aus.

Anfangs- und Randbedingungen

Die Randbedingungen für die Geschwindigkeit U und dem Druck p werden in den beiden Dateien U und p im Unterverzeichnis 0 festgelegt.

 

FoamFile
{
version     2.0;
format      ascii;
class        volVectorField;
object      U;
}  
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

dimensions      [0 1 -1 0 0 0 0];

internalField   uniform (0 0 0);

boundaryField
{
movingWall     
{
type            fixedValue;
value           uniform (1 0 0);
}

    fixedWalls
{
type            fixedValue;
value           uniform (0 0 0);
}

    frontAndBack
{
type            empty;
}
}

 

 

 FoamFile
{
version     2.0;
format      ascii;
class        volScalarField;
object      p;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

dimensions      [0 2 -2 0 0 0 0];

internalField   uniform 0;

boundaryField
{
movingWall
{
type            zeroGradient;
}

    fixedWalls
{
type            zeroGradient;
}

    fixedWalls
{
type            zeroGradient;
}

    frontAndBack
{
type            empty;
}
}

 

 

Im Header steht der Name und Typ der Variable (Skalar oder Vektor). Die Dimensionszuweisung erfolgt nach dem folgenden Schema:

die Vektorpositionen stehen für die SI-Einheiten

[kg m s K mol A cd]

und die Zahlen bedeuten die Potenzen dieser Einheiten, aus der sich die gesamte Einheit zusammensetzt. Für die Geschwindigkeit in m/sec ist dann der Vektor [0 1 -1 0 0 0 0], für den (kinematischen) Druck m2/sec2 entsprechend [0 2 -2 0 0 0 0].

Im Inneren des Kastens sind beide Felder Null, auf den Randflächen ist die Geschwindigkeit 0 bzw. hat auf dem Deckel die Komponenten  (1 0 0), der Druck hat den Wert "zeroGradient", was bedeutet, dass die Normalenkomponente des Druckgradienten dort verschwindet. Aus den Seitenflächen "frontAndBack"sind keine Randbedingungen vorgegeben.

Physikalische Eigenschaften

Als physikalische Eigenschaften sind bei inkompressibler laminarer Strömung newtonscher Fluide nur die kinematische Viskosität anzugeben und diese wird über die Datei transportProperties im Verzeichnis constant festgelegt:

 

 FoamFile
{
version     2.0;
format      ascii;
class       dictionary;
location    "constant";
object      transportProperties;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

nu              nu [ 0 2 -1 0 0 0 0 ] 0.01;

 

  nu hat den Wert 0.01 m2/s.

Diskretisierung und Solver

Die Diskretisierung der Differentialgleichungen werden in der Datei fvSchemes und die Solverparameter werden in der Datei fvSolution festgelegt, die im Unterverzeichnis system liegen. Einzelheiten dazu findet man ebenfalls im User's Guide.

 

FoamFile
{
version     2.0;
format      ascii;
class       dictionary;
location    "system";
object      fvSchemes;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

ddtSchemes
{
default         Euler;
}

gradSchemes
{
default         Gauss linear;
grad(p)         Gauss linear;
}

divSchemes
{
default         none;
div(phi,U)      Gauss linear;
}

laplacianSchemes
{
default         none;
laplacian(nu,U) Gauss linear corrected;
laplacian((1|A(U)),p) Gauss linear corrected;
}

interpolationSchemes
{
default         linear;

    interpolate(HbyA) linear;
}

snGradSchemes
{
default         corrected;
}

fluxRequired
{
default         no;
p               ;
}

 

 

 

FoamFile
{
version     2.0;
format      ascii;
class       dictionary;
location    "system";
object      fvSolution;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

solvers
{
p
{
solver          PCG;
preconditioner  DIC;
tolerance       1e-06;
relTol          0;
}

    U
{
solver          PBiCG;

        preconditioner  DILU;
tolerance       1e-05;
relTol          0;
}
}

PISO
{
nCorrectors     2;
nNonOrthogonalCorrectors 0;
pRefCell        0;
pRefValue       0;
}

 

Start des Solvers

Der Job kann auf interaktive Weise gestartet werden und auch im Batch. Am einfachsten ruft man den Solver direkt auf. In unserem Beispiel ist es der der icoFoam Solver:

icoFoam -case <Pfad>/cavity

oder wenn man sich im Verzeichnis cavity befindet, reicht auch einfach die Eingabe von

icoFoam.

Etwas komfortabler ist der Aufruf

foamJob icoFoam

hier wird automatisch ein Log-File log angelegt, in dem man den Iterationsverlauf und die Residuen beobachten kann. Gibt man danach noch

foamLog log

wird, ein Unterverzeichnis logs angelegt, in dem der zeitliche Verlauf der einzelnen Residuen und globalen Größen getrennt in Dateien gespeichert wird. Diese können dann als xy-Plots angezeigt werden (mit gnuplot, Matlab o.a.).

Auf dem Cluster sollten nach Möglichkeit Jobs, die große Ressourcenanforderungen haben, nicht im Vordergrund gestartet werden, sondern über das Job Mangement System (JMS). Es gibt einen Submit-Mechanismus, der den Job in eine Warteschlange einreiht und die angeforderte Anzahl an Knoten samt nötigem Hauptspeicher reserviert:

job_submit -c p -p n -t time -m memory "foamJob -p icoFoam"

Die einzelnen Parameter von job_submit werden beschrieben, wenn man

job_submit -H

aufruft. Mit job_submit wird das Kommando foamJob -p icoFoam abgeschickt. Die Option -p bedeutet, dass der Job parallelisiert berechnet werden soll. Die Anzahl der parallelen Prozessoren werden durch den Parameter "-p n" im job_submit festgelegt.  Bei serieller Abarbeitung fällt die -p Option weg und in job_submit steht "-p 1".

job_submit -c p -p 1 -t time -m memory "foamJob  icoFoam"

Bei paralleler Abarbeitung muss vorher das Gitter partitioniert werden (s.u.).

Postprocessing

Als Ergebnis erhält man zunächst eine Anzahl an Verzeichnissen, die nach dem Ausgabezeitpunkt benannt sind, so, wie es oben bei den Time Directories der case Struktur beschrieben ist. In unserem Beispiel sind das die Verzeichnisse 0, 0.1, 0.2, .....,0.5 in denen die Ergebnisse zu diesen Zeitpunken stehen.

Der Standardpostprozessor ist Paraview, der in OpenFOAM eingebettet ist. Dazu gibt man im case Unterverzeichnis einfach das Kommando

paraFoam

ein und wählt als Reader OpenFOAM. Genaueres dazu findet man im OpenFOAM User Guide.

Man kann auch Paraview als eigenständigen Postprozessor mit

paraview

aufrufen und eine Fülle an anderen Formaten einlesen, z.B. VTK, EnSight, FLUENT u.v.m.

OpenFOAM selbst bietet einige Konvertierer an, die diese Formate erzeugen. Dazu werden im Unterverzeichnis cavity die verschiedene Datenkonvertierer aufrufen, die die Eingabedaten für verschiedene andere Postprozessoren erzeugen:

  • foamDataToFluent
  • foamToVTK
  • foamToEnsight
  • u.a.

Zum Beispiel wird ein VTK Format erzeugt, in dem man

foamToVTK

aufruft, worauf ein Unterverzeichnis VTK erzeugt wird, in dem die Dateien cavity_0.vtk, cavity_20.vtk, ... cavity_100.vtk stehen. Die Zahlen bedeuten hier nicht das Ergebnis zu Zeitpunkten, sondern nach der Anzahl der Zeitschritte. Der Umgang mit Paraview wird im ParaFOAM User's Guide, ParaView Tutorial und anderen Dokumentationen beschrieben.

Auf analoge Weise werden auch Formate für EnSight, FLUENT u.a. erzeugt.

Mit foamCalc können aus den aus den Ergebnissen weitere, abgeleitete Größen berechnet werden. Ein weiteres nützliches Werkzeug ist die sample Bibliothek. Damit lassen sich Daten entlang einer Linie oder auf einer Fläche sammeln und in ein Format ausgeben, das es erlaubt, sie mit Plotprogrammen wie gnuplot, Matlab o.a. grafisch darzustellen. Voraussetzung ist, dass ein sampleDict im system Unterverzeichnis angelegt wird. Ein Beispiel ist

 

FoamFile
{
version         2.0;
format          ascii;
class           dictionary;
location        system;
object          sampleDict;
}

setFormat raw;

sets
(
lineX1
{
type        uniform;
axis        distance;

        start       (0.02 0.051 0.005);
end         (0.06 0.051 0.005);
nPoints     10;
}
);

surfaces ();

fields   (U.component(1));

 

Hier wird entlang einer Linie mit den Endpunkten (0.02 0.051 0.005) und (0.06 0.051 0.005) an 10 Punkten die y-Komponente der Geschwindigkeit ausgewertet. Es wird ein Unterverzeichnis sets angelegt und die Werte für den XY-Plot in die Unterverzeichnisse 0, 0.1 , ... , 0.5 gelegt. Ein Flächenplot wird nicht angefordert.

Parallelverarbeitung

Zunächst muss das Rechengebiet in Domänen zerlegt werden. Dazu wird wieder ein Dictionary decomposeParDict in das Unterverzeichnis system gelegt. Mit dem Aufruf

decomposePar

erfolgt dann die Partitionierung. Ein Beispiel für eine Zerlegung in 8 Domänen ist die folgende Datei:

 

FoamFile
{
version         2.0;
format          ascii;
class           dictionary;
location        system;
object          sampleDict;
}

numberOfSubdomains 8;

method          simple;

simpleCoeffs
{
n               ( 2 4 1 );
delta           0.001;
}

hierarchicalCoeffs
{
n               ( 1 1 1 );
delta           0.001;
order           xyz;
}

metisCoeffs
{
processorWeights ( 1 1 1 1 );
}

manualCoeffs
{
dataFile        "";
}

distributed     no;

roots           ( );

 

Bei der Methode simple wird die Zelegung durch die Anzahl der Partitionen in jede Richtung durch das Tripel n=(nx,ny,nz) gegeben. Zu den verschiedenen Methoden und die Bedeutung der jeweiligen Koeffizienten methodeCoeffs findet man weitere Informationen im User's Guide.

Nach der Zerlegung erfolgt der Aufruf:

 job_submit -c p -p 8 -t 100 -m 2000 "foamJob -s -p icoFoam"

Im case werden 8 Unterverzeichnisse processor0, .... , processor7 angelegt, jeweils mit den Ergebnisse einer Domäne zu den oben beschriebenen Zeitpunkten. Mit dem Aufruf

reconstructPar

werden die Ergebnisse der einzelnen Domänen wieder zusammengeführt.

Die Zeile

distributed no;

in decomposeParDict besagt, dass von allen Prozessoren auf dasselbe Filesystem zugegriffen wird. Das kann bei großen Problemen und auch bei hohem Parallelisierungsgrad zu Performanceverlusten führen. Es ist in diesen Fällen besser, jeder Prozessor schreibt seine Daten auf ein lokales Filessystem. Dazu ändert man die Zeile in

distributed yes;

Zusätzlich müssen in decomposeParDict weitere Informationen stehen, die aber erst zur Laufzeit des Jobs bekannt sind, die Namen der der temporären lokalen Verzeichnisse. Am Besten erzeugt man sich eine Datei z.B. name_JOB mit folgendem Inhalt:

. set_OF_2.3.0
set_roots
foamJob -s -p icoFoam
reconstructPar

und startet den Job aus dem case-Verzeichnis heraus mit z.B.

job_submit -c p -p 8 -t 100 -m 2000 name_JOB

set_roots ist ein Shellscript, das die temporäten lokalen Verzeichnisse anlegt, ein decomposePar ausführt und anschließend die Unterverzeichnisse processor0, ... ,processor7 in die lokalen Verzeichnisse kopiert. reconstrucPar kopiert die Verzeichnisse processor0, ... ,processor7 nach erfolgter Berechnung wieder in das lokale Filesystem. Da set_roots nur auf einem Knoten laufen kann, also nicht auf mehrere Knoten verteilt, bedeutet das

  1. die Anzahl der Prozessoren (p) darf maximal 8 (HC3) bzw. 16 (IC2) sein
  2. die Hauptspeicheranforderung (m) muss so sein, das das Produkt p*m kleiner/gleich dem maximalen Speicher des Knoten ist, d.h. auf dem KIT-Cluster HC3 für die Partitionen
    • t: 24000 MByte
    • m: 48000 MByte
    • f: 144000 MByte

    auf dem Instituts-Cluster IC2 für die Partitionen

    • t: 64000 MByte
    • m: 64000 MByte
    • f: 512000 MByte
  3. Die Partitionen können im job_submit explizit über die Option -d angefordert werden, ohne Angabe wählt das System die Partition selbständig aus.

 

Um die Parallelisierung auf mehreren Knoten verteilt zu nutzen, ist das Shellscript OF_distPar (OpenFOAM distributed parallel) anzuwenden. Ihre Datei name_JOB sieht dann folgendermaßen aus:

. set_OF_2.3.0
OF_distPar icoFoam
reconstructPar

Der Job wird aus dem case-Verzeichnis heraus wie oben beschrieben submittiert.

Das Shellskript OF_distPar kopiert lediglich das gesamte OpenFoam Modell in die temporären lokalen Verzeichnisse der reservierten Knoten und führt die Berechnung durch. Daher ist der Befehl blockMesh und decomposePar bei kleinen Modellen manuell auf dem Login Knoten vorher auszuführen, oder bei großen Modellen ist das Pre-Processing  als separaten Batch-Job abzuschicken. Bei der Methode der Parallelisierung mit OF_distPar gibt es keine Beschränkung der Anzahl der Prozessoren auf 8 bzw. 16, und die Partitionen müssen nicht explizit über die Option -d angefordert werden. Dadurch bleibt die Wartezeit im Batchsystem der Cluster gering.

OpenFOAM Dokumentation

Die folgenden Links führen zu den Handbüchern und Tutorien:

Viele weitere Referenzen findet man unter www.pdfbe.com/op/openfoam-book.pdf.