instructional workshop on openfoam programming lecture …instructional workshop on openfoam...
TRANSCRIPT
Instructional workshop on OpenFOAMprogramming
LECTURE # 3
Pavanakumar Mohanamuraly
April 26, 2014
Outline
Recap of Week 1
1d Heat Equation
Finite difference
lduMatrix and fvMatrix
FOAM Finite volume - Operators (laplacian)
Recap of Week 1
I Code development compile/running
I Basic FOAM data-structures
I FOAM polyMesh and fvMesh
I FOAM fields
1d Heat equation
∂φ
∂t− κ∂
2φ
∂x2= f (x , t) 0 ≤ x ≤ L, t ≥ 0 (1)
with initial conditions,
φ(x , 0) = g0(x)
and boundary conditions,
I Dirichlet type
φ(0, t) = φ0 and φ(L, t) = φL
I Neumann type
∂φ
∂x(0, t) = φ′0 and
∂φ
∂x(L, t) = φ′L
Robins BC discussed on Day 2 (user defined BCs)
Finite difference (FD) basics 12 FINITE DIFFERENCE METHOD 3
ContinuousPDE forφ(x, t)
Finitedifference−−−−−−−−→
DiscreteDifferenceEquation
Solutionmethod−−−−−−−−→ φm
i approxi-mation to φ(x, t)
Figure 1: Relationship between continuous and discrete problems.
uniformly spaced in the interval 0 ≤ x ≤ L such that
xi = (i − 1)∆x, i = 1, 2, . . . N
where N is the total number of spatial nodes, including those on the boundary.Given L and N , the spacing between the xi is computed with
∆x =L
N − 1.
Similarly, the discrete t are uniformly spaced in 0 ≤ t ≤ tmax:
tm = (m − 1)∆t, m = 1, 2, . . . M
where M is the number of time steps and ∆t is the size of a time step2
∆t =tmax
M − 1.
The solution domain is depicted in Figure 2. Table 1 summarizes the notationused to obtain the approximate solution to Equation (1) and to analyze theresult.
2.2 Finite Difference Approximations
The finite difference method involves using discrete approximations like
∂φ
∂x≈ φi+1 − φi
∆x(3)
2The first mesh lines in space and time are at i = 1 and m = 1 to be consistent with theMatlab requirement that the first row or column index in a vector or matrix is one.
Table 1: Notation
Symbol Meaning
φ(x, t) Continuous solution (true solution).
φ(xi, tm) Continuous solution evaluated at the mesh points.
φmi Approximate numerical solution obtained by solving
the finite-difference equations.
I x discretized as uniformly spaced interval 0 ≤ x ≤ L such that
xi = (i − 1)∆x i = 1, 2, ...N where, ∆x =L
N − 1
I Similarly, discretize t uniformly in 0 ≤ t ≤ T
tm = (m − 1)∆t i = 1, 2, ...M where, ∆t =T
M − 1
φ(x , t)→ φ(xi , tm)→ φmi
1Source: http://www.nada.kth.se/˜jjalap/numme/FDheat.pdf
2nd order central differenceTaylor series expansion
I Forward
φi+1 = φi + ∆x∂φ
∂x
∣∣∣∣xi
+∆x2
2
∂2φ
∂x2
∣∣∣∣xi
+ ... (2)
I Backward
φi−1 = φi −∆x∂φ
∂x
∣∣∣∣xi
+∆x2
2
∂2φ
∂x2
∣∣∣∣xi
− ... (3)
Adding (2) and (3) we get
φi+1 + φi−1 = 2φi + ∆x2 ∂2φ
∂x2
∣∣∣∣xi
+ O(∆x)4 (4)
∂2φ
∂x2
∣∣∣∣xi
=φi+1 − 2φi + φi−1
∆x2+ O(∆x)2 (5)
2nd order central difference
Subtracting (2) and (3) we get
φi+1 − φi−1 = 2∆x∂φ
∂x
∣∣∣∣xi
+ (∆x)3∂3φ
∂x3+ ... (6)
∂φ
∂x
∣∣∣∣xi
=φi+1 − φi−1
2∆x+ O(∆x)2 (7)
Alternatively, using equation (2),
∂φ
∂x
∣∣∣∣xi
=φi+1 − φi
∆x+ O(∆x) (8)
and equation (3),
∂φ
∂x
∣∣∣∣xi
=φi − φi−1
∆x+ O(∆x) (9)
Boundary condition
I Dirichlet typeφi=1 = φ0 φi=N = φL
I Neumann type (1st order)
φ1 = φ2 − φ′0∆x and φN = φN−1 + φ′L∆x
I Neumann type (2nd order)
φi=0 = φ2 − 2φ′0∆x and φN+1 = φN − 2φ′L∆x
I Halo nodes i = 0 and i = N + 1
Make FD look like FV - owner/neighbour
I Need cell-to-cell connectivity for FOAM FD
I Avoid this by modifying FD implementation
I Original domain
i=Ni=1
i=Ni=1
RLL R L R L R+1/2-1/2
I Modified domain (half points - faces)
i=Ni=1
i=Ni=1
R L
Make FD look like FV - owner/neighbour
I Modified domain
i=Ni=1
i=Ni=1
R L
I Split into face contribution (interior faces)
∂2φ
∂x2
∣∣∣∣xi
=φi+1 − 2φi + φi−1
∆x2=
1
∆x
φi+1 − φi
∆x︸ ︷︷ ︸L
− φi − φi−1∆x︸ ︷︷ ︸R
(10)
φi+1 − φi
∆x=
1
∆x
φi+1︸︷︷︸L
− φi︸︷︷︸R
(11)
φi − φi−1∆x
=1
∆x
φi︸︷︷︸L
−φi−1︸︷︷︸R
(12)
Make FD look like FV - owner/neighbour
Sign consistency examples (ignore ∆x for clarity)
I Case (i)
i=Ni=1
i=Ni=1
R L
i
RL
f1 f2
R L
i-1 i+1
RL
f1 f2
L R
ii-1 i+1
f1 = φL − φR = φi−1 − φi (13)
f2 = φL − φR = φi − φi+1 (14)
−f1︸︷︷︸R
+ f2︸︷︷︸L
= −(φi+1 − 2φi + φi−1) = − ∂2φ
∂x2
∣∣∣∣xi
(15)
Make FD look like FV - owner/neighbour
I Case (ii)
i=Ni=1
i=Ni=1
R L
i
RL
f1 f2
R L
i-1 i+1
RL
f1 f2
L R
ii-1 i+1
f1 = φL − φR = φi−1 − φi (16)
f2 = φL − φR = φi+1 − φi (17)
−f1︸︷︷︸R
− f2︸︷︷︸R
= −(φi+1 − 2φi + φi−1) = − ∂2φ
∂x2
∣∣∣∣xi
(18)
I As long as L/R is consistently defined we get the correctexpression
Make FD look like FV - owner/neighbour
Neumann boundary conditions
I Remember the fact that
φ′0 =φ1 − φ0
∆x= φf
1 and φ′L =φN+1 − φN
∆x= φf
N (19)
where, f denotes the face value
I Simply enforce values φ′0 and φ′L for the Neumann patch face
Make FD look like FV - owner/neighbour
Dirichlet boundary conditions
I Patch face value set to enforces correct values (weak)I Introduce halo nodes at both ends
I At i = NφN+1 − φN
∆x=
1
∆x(φL − φN ) (20)
I At i = 1φi=1 − φi=0
∆x=
1
∆x(φ1 − φ0) (21)
I Remember that if dy and dz is = 1 then ∆x = ∆V , where∆V is cell volume
Make FD look like FV - owner/neighbour
Sign consistency for boundary (ignore ∆x for clarity)
I Case (i) At x = 0
i = 1LR
fx=0
R L
i = 0 i = 2
I Dirichlet type
fx=0 =φL − φR
∆x=φ1 − φx=0
∆x(22)
I Neumann typefx=0 = φ′0 (23)
Make FD look like FV - owner/neighbour
Sign consistency for boundary (ignore ∆x for clarity)
I Case (i) At x = 0
i = NRL
fx=L
L R
i = N+1
I Dirichlet type
fx=L =φL − φR
∆x=φN − φN+1
∆x(24)
I Neumann typefx=L = φ′L (25)
How to access field boundary values ?
I fixedValue patch type
const tmp<scalarField> sf;scalarField some( x.boundaryField()[ipatch].
valueBoundaryCoeffs(sf) );
I fixedGradient patch type
scalarField some( x.boundaryField()[ipatch].gradientBoundaryCoeffs() );
Hands on - FD solver I
I For convenience set dx = dy = dz = 1 in the 1d grid
I Ignore time term for now and solve for steady-state
I Create a volScalarField phi and read from input file
I Create a surfaceScalarField named flux
I Loop over all internal faces of flux and set face value
φf = φowner − φneighbour (26)
I For Neumann patches set face value to the gradient
φf = φ′(x = 0 or x = L) (27)
I For Dirichlet patches set face value using
φf = φowner − φ(x = 0 or x = L) (28)
Hands on - FD solver I
I Create a volScalarField named residue and initialize to zero
I Loop over all faces of flux
I Add the face value to the owner cell and subtract fromneighbour cell of residue
I Do the same for boundary faces (no neighbour cell)
I Now residue contains the laplacian
Matrix forms
I Possible to construct matrix version of the discrete operator
Matrix form of discrete system of Poisson Equations
1 2 3 4 5 6 7 8 9 10
1 −1 1 × × × × × × × ×2 1 −2 1 × × × × × × ×3 × 1 −2 1 × × × × × ×4 × × 1 −2 1 × × × × ×5 × × × 1 −2 1 × × × ×6 × × × × 1 −2 1 × × ×7 × × × × × 1 −2 1 × ×8 × × × × × × 1 −2 1 ×9 × × × × × × × 1 −2 110 × × × × × × × × 1 −1
x1x2x3x4x5x6x7x8x9x10
=
f1f2f3f4f5f6f7f8f9f10
I BC enforced separately using boundaryCoeff and internalCoeff
I For zeroGradient BC this matrix becomes the full system
I Most of the entries of A are zeros =⇒ A is sparse
Sparse Matrix Storage in FOAM
I FOAM uses the LDU sparse matrix storage
I Uses the owner/neighbour data for addressing non-zero L/Uentries
I Optimized for symmetric matrices also handles asymmetricmatrix
I Key limitation “cannot store entries beyond first level of cellneighbours”
I Big issue for hybrid and highly skewed meshesI Higher order FVM (> 2nd order)
lduMatrix
I Abstract base class implementing the ludMatix and solvers
I Uses the lduMatrix to construct the owner/neighbouraddressing scheme
/// Construct given an LDU addressed mesh.lduMatrix (const lduMesh &)/// Construct given an LDU addressed mesh and an
Istream.lduMatrix (const lduMesh &, Istream &)
I lduMatrix is not usable, need non-abstract class fvMatrix
I FOAM operators like grad , div , etc, return fvMatrix
fvMatrix
fvMesh
fvMatrix
field source solver
Matrix CoeffslduAddressing
I Solves the system of equations
Ax = b (29)
where, x is the field , b is the source and sparse matrix Aentries are Matrix Coeffs.
I Subject to BC specified on the field using a specified solver
fvm and fvc namespace
I Two possible ways to construct discrete operatorsI Matrix-free scoped in fvc namespaceI Matrix-based scoped in fvm namespace
I Explicit matrix construction impossible for non-linearoperations like limiting
I Operators defined in fvc return field types
I Operators defined in fvm return fvMatrix types
Krylov solvers 2
I For large sparse systems it only possible to performmatrix-vector products (Ax)
I Even explicit construction of matrix A is impossible
I But it is possible to construct a Krylov sequence{x,Ax,A2x,A3x, ...
}I Krylov sub-space methods build up on the sequence and look
forI Eigenvectors andI Invariant sub-spaces
within the Krylov sub-space
I A large class of LA algorithms like CG, BICG, GMRES arebased on Krylov sub-spaces
I Preconditioning is normally performed to speed upconvergence
2Source:‘‘The Matrix Eigenvalue Problem” by David S. Watkins
Krylov sub-space solvers in fvMatrix
I Krylov sub-space solvers available for fvMatrixI Symmetric matrix
I GAMG - Geometric/Algebraic Multi-Grid solverI ICCG - Incomplete Cholesky Conjugate GradientI PCG - Preconditioned Conjugate Gradient solver
I Asymmetric matrixI BICCG - Bi-Conjugate GradientI GAMG - Geometric/Algebraic Multi-Grid solverI PBiCG - Preconditioned Bi-Conjugate Gradient
I Matrix preconditionersI DIC - Diagonal Incomplete CholeskyI DILU - Diagonal Incomplete LUI GAMG - Geometric/Algebraic Multi-Grid solver
Multigrid and Krylov solver beyond the scope of the workshop
How to use fvMatrix ?
I Will look at the specific constructor using field variable
fvScalarMatrix A( x, x.dimensions() );
I Access non-zero upper diagonal entries using A.upper()
I Access diagonal entries using A.diag()
I Access non-zero lower diagonal entries using A.lower()I First access to A.upper() after construction makes A
symmetricI lowerPtr = upperPtr ;
I Since fvMatrix entries are based onmesh.owner()/neighbour() following holds true
I Size of diag().size() = number of cellsI Size of upper()/lower().size() = number of internal faces
negDiagSum() function
I Negated row-wise sum of non-zero off diagonal coeffs
void Foam::lduMatrix::negSumDiag(){const scalarField& Lower
= const_cast<const lduMatrix &>(*this).lower();const scalarField& Upper
= const_cast<const lduMatrix &>(*this).upper();scalarField& Diag = diag();
const labelUList& l = lduAddr().lowerAddr();const labelUList& u = lduAddr().upperAddr();
for (register label face=0; face<l.size(); face++){
Diag[l[face]] -= Lower[face];Diag[u[face]] -= Upper[face];
}}
Hands on - Create FD matrix A from example
I Ignore BC patches for now
I Access A.upper() and assign that to 1
I Calculate the diagonal term using the negSumDiag() function
I Now check if the matrix is symmetric using A.symmetric()
I It should return true or false ?
Hints
I A.upper()/diag() is a scalarField so forAll works
I Lazy yet efficient option is to use assignment operator =
Specifying the BC
The BC is specified using two fields
A.boundaryCoeffs()
I Source term that goes to the RHS
I For Neumann condition the −fixedGradient value is specified
I For Dirichlet condition the −fixedValue divided by ∆x
A.internalCoeffs()
I Term that goes to the LHS matrix coefficient
I For Neumann condition it is zero
I For Dirichlet condition it is −1 divided by ∆x
Show on black-board how this calculated
Access boundary values (fixedValue)
I Access field connected to A using A.psi()
forAll( A.psi().boundaryField(), ipatch ) {if(A.psi().boundaryField()[ipatch].type()== "fixedValue"
){const tmp<scalarField> sf;scalarField value(
A.psi().boundaryField()[ipatch].valueBoundaryCoeffs(sf)
);A.boundaryCoeffs()[ipatch] = -1.0 * value;A.internalCoeffs()[ipatch] = -1.0;
}
Access boundary values (fixedGradient)
if(A.psi().boundaryField()[ipatch].type()== "fixedGradient"
){Info << ipatch << " fixedGradient ";scalarField gradient(
A.psi().boundaryField()[ipatch].gradientBoundaryCoeffs()
);A.boundaryCoeffs()[ipatch] = -1.0 * gradient;Info << " gradient = " << gradient << "\n";
}
Access boundary values (zeroGradient)
if(A.psi().boundaryField()[ipatch].type()== "zeroGradient"
){Info << "Patch " << ipatch << ": is zeroGradient\n
";}
How to access field boundary values ?
I Number of coefficients equal the number of faces in the patch
I Consequence of the fact that it is possible to have differentvalues for each face in the patch
I Achieved using the nonuniform key word in the field dictionary
I Inlet velocity profiles for pipe flows
Hands on: Print boundary field type and value
I Loop over all patches of the field x
I Print the type of boundary and boundary value
I Set correct values for A.boundaryCoeffs()/internalCoeffs()
Solving the LA problem
I So far we have only discussed setting up Ax
I What about b ?
I It is accessed using A.source()
I If everything is set then we are ready to solve the LA problem
Ax = b (30)
Solving the LA problem
I So far we have only discussed setting up Ax
I What about b ?
I It is accessed using A.source()
I If everything is set then we are ready to solve the LA problem
Ax = b (30)
The solution dictionary
system/fvSolution dictionary
solvers{x // The field variable name{
solver PCG;preconditioner none;tolerance 1e-06;relTol 0;
}}
Hands on - Post-process 1d data
I 1d case is actually 3d with once cell thickness
I Print the result to a file profile.dat
#include <fstream>....std::ofstream fout("initial_sol.dat");forAll( mesh.C(), i )fout << mesh.C()[i][0] << " " << x[i] << "\n";
I Visualize using gnuplot
gnuplot> plot "initial_sol.dat" using 1:2 with lines
Hands on - Using fvMatrix for solution
I Solve the 1d FD problem without time derivative usingfvMatrix
I Proper boundary condition and value should be set
I Choose appropriate solver (write solver dictionary)
I Set the A.source() to zero
I Essentially solving the equation
∂2φ
∂x2= 0 (31)
I Should give a straight line profile if Dirichlet BC is specifiedon both ends (use the previous hand-on to visualize results ingnuplot)
Finite Volume (FV) discretization of 1d heat equation
I FV domain
i=Ni=1
i=Ni=1
R L
I Split into face contribution (interior faces)
∂2φ
∂x2
∣∣∣∣xi
=φi+1 − 2φi + φi−1
∆x2=
1
∆x
φi+1 − φi
∆x︸ ︷︷ ︸L
− φi − φi−1∆x︸ ︷︷ ︸R
(32)
φi+1 − φi
∆x=
1
∆x
φi+1︸︷︷︸L
− φi︸︷︷︸R
(33)
φi − φi−1∆x
=1
∆x
φi︸︷︷︸L
−φi−1︸︷︷︸R
(34)
Finite Volume (FV) discretization of 1d heat equation
I It is not a surprise that we end up with the same set ofequations
I FV and FD give the same discretization for equally spacedgrids in this case
I But this time we make use of FOAM operators to make thejob simple
FOAM operators - laplacianSchemeI fvm :: laplacian is an in-built operator which yields correct
fvMatrixI Need to define the laplacianScheme in the fvSchemes
dictionary
laplacianSchemes{/// Green-gauss type with/// non-orthogonality correctiondefault Gauss linear corrected;
}
I Constructor requires only the field as argument
fvScalarMatrix A( fvm::laplacian(x) );A.source() = 0.0;A.solve();
Hands on - FOAM’ish solver
I Implement the FV solver with similar inputs and equations asthe FD hands on
I Plot 1d solution using gnuplot
End of Week 2 Day 1