"The lesser the people know about how sausages and laws are made, the better they sleep in the night"
Otto v. Bismarck
Bismarck knew nothing about presentations.
This presentation is about how it was made
Who is this Bismarck anyway?
The aim of this presentation is to demonstrate two new features of PyFoam
Are usually announced:
foam-extend
has a p-U-coupled solver pUCoupledFoam
pUCoupledFoam
pitzDaily
: a backward facing step for which measurment data by Pitz and Daily existsfrom IPython.display import Image
Image(filename="pitzDailyOverview.png")
We like both types of music: we like Country and Western
Jake & Elwood Blues
We're going to compare:
coupled31
: pUCoupledFoam
from foam-extend-3.1
segregated31
: simpleFoam
from foam-extend-3.1
segregated23
: simpleFoam
from OpenFOAM-2.3
coupled23
: simpleFoam
from OpenFOAM-2.3
with the coupled option for the U-EqnpitzDaily
from foam-extend-3.1
OpenFOAM-2.3
the fvSchemes
from OF-2.3 will be usedpUCoupledFoam
the fvSolution
from the tutorial case was usedImage(filename='pitzDailyDetachmentPoint.png')
Image(filename="pitzDailyMeasurments.png")
pitzDaily
-cases are similar on OpenFOAM-2.3
and foam-extend-3.1
#ifdef
but more flexibleAllrun
is only good at 3 and 4changeDictionary
pyFoamPrepareCase.py
utility tries to automatize this0.org
is present remove that too.template
do template expansionmeshCreate.sh
is found execute thisblockMesh
foo.org
(file or directory) to foo
.postTemplate
caseSetup.sh
if presentThat is the easy part: the case is so simple we don't need no scripts
And you know how to write scripts, right?
PyFoam
for a long timepyratemp
(http://www.simple-is-better.org/template/pyratemp.html)PyFoam
- no need to installPyFoam
is modified and allows things that on the original application field (webservers) would be insanely dangerousPyFoam
-specific extensions|-
and -|
is evaluated as a Python-expression and the result is inserted$$
are discardedpitzDaily
-case that comes with OpenFOAM specifies $10 \frac{m}{s}$ as the inlet velocityIn 0.org/U.template
:
boundaryField
{
inlet
{
type fixedValue;
value uniform (|- UIn -| 0 0);
}
is expanded to (assuming UIn
is $13.3$)
boundaryField
{
inlet
{
type fixedValue;
value uniform (13.3 0 0);
}
pyratemp
-specific commands start with <!--(
and end with )-->
<!--(if expr)-->
starts a block that is kept if expr
is True
elif
and else
are also possible<!--(end)-->
For details see documentation
In fvSolution.template
incompatible relaxation factors are set:
relaxationFactors
{
<!--(if pUCoupled and not of23)-->
U 0.9;
k 0.95;
epsilon 0.95;
<!--(elif not of23)-->
p 0.3;
U 0.7;
k 0.7;
epsilon 0.7;
R 0.7;
nuTilda 0.7;
<!--(else)-->
fields
{
p 0.3;
}
equations
{
U 0.7;
k 0.7;
epsilon 0.7;
R 0.7;
nuTilda 0.7;
}
<!--(end)-->
}
pUCoupledFoam
expansion¶If pUCoupled
is set to True
and of23
is False
this becomes:
relaxationFactors
{
U 0.9;
k 0.95;
epsilon 0.95;
}
pyFoamPrepareCase.py
--values-string
--parameter-file
For example parameters.coupled31
:
#include "parameters.basic"
pUCoupled on;
of23 off;
This parameter-set is used with
pyFoamPrepareCase.py . --parameter-file=parameteres.segregated31
swak4Foam
swak4Foam
pyFoamPlotRunner.py
Is there an OpenSource-solution for this?
python
is a programming languagenumpy
for matricessympy
for symbolic compuationsscipy
for numerical algorithmsmatplotlib
for plottingpandas
for convenient handling of data-setsipython
is a shell for python
ipython
-notebooks are a browser-based interface to thisPyFoam
from an ipython
-notebookpandas
DataFrame
that adds convenience featuresCase
-class for easily calling functionality to import dataNotebook
class to write notebooks from a python
-programAll this is integrated into one utility
Case
-object that points to the current caseNow this is the moment where Bernhard says "I'll change to the shell and show you"
He'll show:
If you read this presentation on the internet: wait 4 minutes before continuing
This section shows some example evaluation. First we set up the environment (remember: most of this is done by pyIPythonNotebook.py
for you)
%matplotlib inline
import matplotlib
matplotlib.rcParams['figure.figsize'] = (10.0, 8.0)
from PyFoam.Wrappers.Pandas import PyFoamDataFrame as DataFrame
from PyFoam.IPython.Case import Case
Now we get us the four cases
case=Case('/Volumes/Foam/Cases/Pfau2014/pitzDaily_coupled31')
case23=Case('/Volumes/Foam/Cases/Pfau2014/pitzDaily_coupled23')
caseSeg=Case('/Volumes/Foam/Cases/Pfau2014/pitzDaily_segregated31')
caseSeg23=Case('/Volumes/Foam/Cases/Pfau2014/pitzDaily_segregated23')
case.size(level=3)
Faces | 49180 |
Points | 25012 |
Cells | 12225 |
case.boundaryConditions(level=3)
frontAndBack | inlet | lowerWall | outlet | upperWall | |
---|---|---|---|---|---|
Patch Type | empty | patch | wall | patch | wall |
Length | 24450 | 30 | 250 | 57 | 223 |
R | empty | fixedValue | zeroGradient | zeroGradient | zeroGradient |
U | empty | fixedValue | fixedValue | zeroGradient | fixedValue |
epsilon | empty | fixedValue | epsilonWallFunction | zeroGradient | epsilonWallFunction |
epsilon.old | empty | fixedValue | epsilonWallFunction | zeroGradient | epsilonWallFunction |
k | empty | fixedValue | kqRWallFunction | zeroGradient | kqRWallFunction |
k.old | empty | fixedValue | kqRWallFunction | zeroGradient | kqRWallFunction |
nuTilda | empty | fixedValue | zeroGradient | zeroGradient | zeroGradient |
nut | empty | calculated | nutWallFunction | calculated | nutWallFunction |
p | empty | zeroGradient | zeroGradient | fixedValue | zeroGradient |
dataCouple=case.pickledPlots('PyFoamRunner.pUCoupledFoam.analyzed/pickledPlots')
dataCouple23=case23.pickledPlots('PyFoamRunner.simpleFoam.analyzed/pickledPlots')
dataSeg=caseSeg.pickledPlots('PyFoamRunner.simpleFoam.analyzed/pickledPlots')
dataSeg23=caseSeg23.pickledPlots('PyFoamRunner.simpleFoam.analyzed/pickledPlots')
Which datasets are there?
dataCouple.keys()
['continuity', 'deltaP', 'linear', 'detachement', 'iterations', 'execution']
Simple plotting
dataCouple["iterations"].plot()
<matplotlib.axes.AxesSubplot at 0x10d19aad0>
This looks worse than it is (renaming datasets for merging):
linCouple=DataFrame(dataCouple["linear"].select(lambda k:(k.find("_")<0 and k!="Up[2]"),1))
linCouple23=DataFrame(dataCouple23["linear"].select(lambda k:(k.find("_")<0 and k.find("Uz")<0),1).rename(columns=lambda n:n+"_cou23"))
linSeg=DataFrame(dataSeg["linear"].select(lambda k:(k.find("_")<0),1).rename(columns=lambda n:n+"_seg"))
linSeg23=DataFrame(dataSeg23["linear"].select(lambda k:(k.find("_")<0),1).rename(columns=lambda n:n+"_seg23"))
linSeg.addData(linCouple).plot(logy=True)
<matplotlib.axes.AxesSubplot at 0x10d15ba50>
Lets have a closer look:
linSeg.addData(linCouple).plot(logy=True,xlim=(0,400))
<matplotlib.axes.AxesSubplot at 0x10d0e55d0>
Seems like coupled converges really faster
OpenFOAM-2.3
and foam-extend-3.1
(segregated)¶linSeg.addData(linSeg23).plot(logy=True)
<matplotlib.axes.AxesSubplot at 0x10d0c6f10>
I don't dare to say who is winning
linSeg23.addData(linCouple23).plot(logy=True)
<matplotlib.axes.AxesSubplot at 0x10cfee2d0>
There is a difference
Caution: These numbers were not gathered under serious benchmarking conditions (there were other things going on on that machine. So the numbers may be skewed)
The calculation times per second are accumulated to get the total calculation time:
DataFrame(dataCouple["execution"].rename(columns=lambda n:n+" coupled")).addData(
dataSeg["execution"].rename(columns=lambda n:n+" segregated")).cumsum().plot()
<matplotlib.axes.AxesSubplot at 0x10d00ab50>
This looks bad for coupled but he converged near $t=550$ and segregated is not nearly finished.
Let's see how the properties of the flow converge
Clean unfit data:
dataSeg["detachement"][dataSeg["detachement"]<-1000]=float("NaN")
Plot:
DataFrame(dataCouple["detachement"].rename(columns={"x":"coupled"})).addData(
dataSeg["detachement"].rename(columns={"x":"segregated"})).plot()
<matplotlib.axes.AxesSubplot at 0x10d054a50>
Seems that converged long before the residuals
Renaming data for merging
deltaPData=DataFrame(dataCouple["deltaP"].rename(columns={"avg":"coupled"})).addData(
dataSeg["deltaP"].rename(columns={"avg":"segregated"}))
deltaPData.plot()
<matplotlib.axes.AxesSubplot at 0x10d059590>
Strong oscillations in the beginning. Try without them:
deltaPData.plot(xlim=(100,1000),ylim=(-20,0))
<matplotlib.axes.AxesSubplot at 0x10d9e2090>
This too converges faster
But how big are the oscillations?
deltaPData.describe()
coupled | segregated | |
---|---|---|
count | 999.000000 | 999.000000 |
mean | -9.092461 | -5.414749 |
std | 7.353295 | 309.862965 |
min | -167.096000 | -4124.230000 |
25% | -9.077380 | -10.544450 |
50% | -9.075930 | -9.775140 |
75% | -9.075830 | -8.965875 |
max | 117.733000 | 6731.340000 |
integral | -9137.696661 | -8770.111473 |
valid length | 998.000000 | 998.000000 |
weighted average | -9.156009 | -8.787687 |
11 rows × 2 columns
More than one order of magnitude smaller for coupled!
Reading the profile data from the experiment:
refU1=case.sampleTime('./experimentReference','experiment1','0')
refU3=case.sampleTime('./experimentReference','experiment3','0')
refU6=case.sampleTime('./experimentReference','experiment6','0')
Reading the sample data from the cases
sample1Couple=case.sampleTime('./experimentSample','experiment1','1000')
sample3Couple=case.sampleTime('./experimentSample','experiment3','1000')
sample6Couple=case.sampleTime('./experimentSample','experiment6','1000')
sample1Couple=sample1Couple.rename(columns=lambda n:n.replace("experiment1_t=1000","coupled"))
sample3Couple=sample3Couple.rename(columns=lambda n:n.replace("experiment3_t=1000","coupled"))
sample6Couple=sample6Couple.rename(columns=lambda n:n.replace("experiment6_t=1000","coupled"))
sample1Seg=caseSeg.sampleTime('./experimentSample','experiment1','1000')
sample3Seg=caseSeg.sampleTime('./experimentSample','experiment3','1000')
sample6Seg=caseSeg.sampleTime('./experimentSample','experiment6','1000')
sample1Seg=sample1Seg.rename(columns=lambda n:n.replace("experiment1_t=1000","segregated"))
sample3Seg=sample3Seg.rename(columns=lambda n:n.replace("experiment3_t=1000","segregated"))
sample6Seg=sample6Seg.rename(columns=lambda n:n.replace("experiment6_t=1000","segregated"))
sample1Seg23=caseSeg23.sampleTime('./postProcessing/experimentSample','experiment1','1000')
sample3Seg23=caseSeg23.sampleTime('./postProcessing/experimentSample','experiment3','1000')
sample6Seg23=caseSeg23.sampleTime('./postProcessing/experimentSample','experiment6','1000')
sample1Seg23=sample1Seg23.rename(columns=lambda n:n.replace("experiment1_t=1000","segregated 2.3"))
sample3Seg23=sample3Seg23.rename(columns=lambda n:n.replace("experiment3_t=1000","segregated 2.3"))
sample6Seg23=sample6Seg23.rename(columns=lambda n:n.replace("experiment6_t=1000","segregated 2.3"))
DataFrame(DataFrame(DataFrame(sample1Couple["coupled magU"]).addData(
refU1,mergeIndex=True)).addData(sample1Seg["segregated magU"],mergeIndex=True)).addData(
sample1Seg23["segregated 2.3 magU"],mergeIndex=True).plot()
<matplotlib.axes.AxesSubplot at 0x10db3e150>
DataFrame(DataFrame(DataFrame(sample3Couple["coupled magU"]).addData(
refU3,mergeIndex=True)).addData(sample3Seg["segregated magU"],mergeIndex=True)).addData(
sample3Seg23["segregated 2.3 magU"],mergeIndex=True).plot()
<matplotlib.axes.AxesSubplot at 0x10dec8050>
DataFrame(DataFrame(DataFrame(sample6Couple["coupled magU"]).addData(
refU6,mergeIndex=True)).addData(sample6Seg["segregated magU"],mergeIndex=True)).addData(
sample6Seg23["segregated 2.3 magU"],mergeIndex=True).plot()
<matplotlib.axes.AxesSubplot at 0x10dcbc950>
(sample1Couple["coupled magU"]-sample1Seg["segregated magU"]).plot()
<matplotlib.axes.AxesSubplot at 0x10deed450>
(sample1Seg23["segregated 2.3 magU"]-sample1Seg["segregated magU"]).plot()
<matplotlib.axes.AxesSubplot at 0x10df38c50>
pUCoupledFoam
solver was compared with simpleFoam
pUCoupledFoam
-solverfoam-extend-3.1
available at http://sourceforge.net/p/openfoam-extend/wiki/Home/ (get the nextRelease
-branch in the foam-extend-3.1
-git