Tutorial 2. Solving a 1D diffusion equation¶
[ ]:
# Document Author: Dr. Vishal Sharma
# Author email: sharma_vishal14@hotmail.com
# License: MIT
# This tutorial is applicable for NAnPack version 1.0.0-alpha4
I. Background¶
The objective of this tutorial is to present the step-by-step solution of a 1D diffusion equation using NAnPack such that users can follow the instructions to learn using this package. The numerical solution is obtained using the Forward Time Central Spacing (FTCS) method. The detailed description of the FTCS method is presented in Section IV of this tutorial.
II. Case Description¶
We will be solving a classical probkem of a suddenly accelerated plate in fluid mechanicas which has the known exact solution. In this problem, the fluid is bounded between two parallel plates. The upper plate remains stationary and the lower plate is suddenly accelerated in y-direction at velocity \(U_o\). It is required to find the velocity profile between the plates for the given initial and boundary conditions.
(For the sake of simplicity in setting up numerical variables, let’s assume that the x-axis is pointed in the upward direction and y-axis is pointed along the horizontal direction as shown in the schematic below:
Initial conditions
Boundary conditions
Specify the required simulation inputs based on our setup in the configuration file provided with this package. You may choose to save the configuration file with any other filename. I have saved the configuration file in the “input” folder of my project directory such that the relative path is ./input/config.ini
.
III. Governing Equation¶
The governing equation for the given application is the simplified for the the Navies-Stokes equation which is given as:
This is the diffusion equation model and is classified as the parabolic PDE.
IV. FTCS method¶
The forward time central spacing approximation equation in 1D is presented here. This is a time explicit method which means that one unknown is calculated using the known neighbouring values from the previous time step. Here i represents grid point location, n+1 is the future time step, and n is the current time step.
The order of this approximation is \([(\Delta t), (\Delta x)^2]\)
The diffusion number is given as \(d_{x} = \nu\frac{\Delta t}{(\Delta x)^2}\) and for one-dimensional applications the stability criteria is \(d_{x}\leq\frac{1}{2}\)
The solution presented here is obtained using a diffusion number = 0.5 (CFL = 0.5 in configuration file). Time step size will be computed using the expression of diffusion number. Beginners are encouraged to try diffusion numbers greater than 0.5 as an exercise after running this script.
Users are encouraged to read my blogs on numerical methods - link here.
V. Script Development¶
Please note that this code script is provided in file ``./examples/tutorial-02-diffusion-1D-solvers-FTCS.py``.
As per the Python established coding guidelines PEP 8, all package imports must be done at the top part of the script in the following sequence – 1. import standard library 2. import third party modules 3. import local application/library specific
Accordingly, in our code we will importing the following required modules (in alphabetical order). If you are using Jupyter notebook, hit Shift + Enter
on each cell after typing the code.
[2]:
import matplotlib.pyplot as plt
from nanpack.benchmark import ParallelPlateFlow
import nanpack.preprocess as pre
from nanpack.grid import RectangularGrid
from nanpack.parabolicsolvers import FTCS
import nanpack.postprocess as post
As the first step in simulation, we have to tell our script to read the inputs and assign those inputs to the variables/objects that we will use in our entire code. For this purpose, there is a class RunConfig
in nanpack.preprocess
module. We will call this class and assign an object (instance) to it so that we can use its member variables. The RunConfig
class is written in such a manner that its methods get executed as soon as it’s instance is created. The users must provide the
configuration file path as a parameter to RunConfig
class.
[3]:
FileName = "path/to/project/input/config.ini" # specify the correct file path
cfg = pre.RunConfig(FileName) # cfg is an instance of RunConfig class which can be used to access class variables. You may choose any variable in place of cfg.
*******************************************************
*******************************************************
Starting configuration.
Searching for simulation configuration file in path:
"D:/MyProjects/projectroot/nanpack/input/config.ini"
SUCCESS: Configuration file parsing.
Checking whether all sections are included in config file.
Checking section SETUP: Completed.
Checking section DOMAIN: Completed.
Checking section MESH: Completed.
Checking section IC: Completed.
Checking section BC: Completed.
Checking section CONST: Completed.
Checking section STOP: Completed.
Checking section OUTPUT: Completed.
Checking numerical setup.
User inputs in SETUP section check: Completed.
Accessing domain geometry configuration: Completed
Accessing meshing configuration: Completed.
Calculating grid size: Completed.
Assigning COLD-START initial conditions to the dependent term.
Initialization: Completed.
Accessing boundary condition settings: Completed
Accessing constant data: Completed.
Calculating time step size for the simulation: Completed.
Calculating maximum iterations/steps for the simulation: Completed.
Accessing simulation stop settings: Completed.
Accessing settings for storing outputs: Completed.
**********************************************************
CASE DESCRIPTION SUDDENLY ACC. PLATE
SOLVER STATE TRANSIENT
MODEL EQUATION DIFFUSION
DOMAIN DIMENSION 1D
LENGTH 0.04
GRID STEP SIZE
dX 0.001
TIME STEP 0.002
GRID POINTS
along X 41
DIFFUSION CONST. 2.1700e-04
DIFFUSION NUMBER 0.5
TOTAL SIMULATION TIME 1.08
NUMBER OF TIME STEPS 468
START CONDITION COLD-START
**********************************************************
SUCEESS: Configuration completed.
You will obtain several configuration messages on your output screen so that you can verify that your inputs are correct and that the configuration is successfully completed. Next step is the assignment of initial conditions and the boundary conditions. For assigning boundary conditions, I have created a function BC()
which we will be calling in the next cell. I have included this function at the bottom of this tutorial for your reference. It is to be noted that U is the dependent variable
that was initialized when we executed the configuration, and thus we will be using cfg.U
to access the initialized U. In a similar manner, all the inputs provided in the configuration file can be obtained by using configuration class object cfg.
as the prefix to the variable names. Users are allowed to use any object of their choice.
If you are using Jupyter Notebook, the function BC must be executed before referencing to it, otherwise, you will get an error. Jump to the bottom of this notebook where you see code cell # 1 containing the ``BC()`` function
[4]:
# Assign initial conditions
cfg.U[0] = 40.0
cfg.U[1:] = 0.0
# Assign boundary conditions
U = BC(cfg.U)
Next, we will be calculating location of all grid points within the domain using the function RectangularGrid()
and save values into X. We will also require to calculate diffusion number in X direction. In nanpack, the program treats the diffusion number = CFL for 1D applications that we entered in the configuration file, and therefore this step may be skipped, however, it is not the same in two-dimensional applications and therefore to stay consistent and to avoid confusion we will be using
the function DiffusionNumbers()
to compute the term diffX
.
[5]:
X, _ = RectangularGrid(cfg.dX, cfg.iMax)
Uniform rectangular grid generation in cartesian coordinate system: Completed.
[6]:
diffX,_ = pre.DiffusionNumbers(cfg.Dimension, cfg.diff, cfg.dT, cfg.dX)
Calculating diffusion numbers: Completed.
Next, we will initialize some local variables before start the time stepping:
[7]:
Error = 1.0 # variable to keep track of error
n = 0 # variable to advance in time
Start time loop using while loop such that if one of the condition returns False, the time stepping will be stopped. For explanation of each line, see the comments. Please note the identation of the codes within the while loop. Take extra care with indentation as Python is very particular about it.
[8]:
while n <= cfg.nMax and Error > cfg.ConvCrit: # start loop
Error = 0.0 # reset error to 0.0 at the beginning of each step
n += 1 # advance the value of n at each step
Uold = U.copy() # store solution at time level, n
U = FTCS(Uold, diffX) # solve for U using FTCS method at time level n+1
Error = post.AbsoluteError(U, Uold) # calculate errors
U = BC(U) # Update BC
post.MonitorConvergence(cfg, n, Error) # Use this function to monitor convergence
post.WriteSolutionToFile(U, n, cfg.nWrite, cfg.nMax,\
cfg.OutFileName, cfg.dX) # Write output to file
post.WriteConvHistToFile(cfg, n, Error) # Write convergence log to history file
ITER ERROR
---- -----
10 4.92187500
20 3.52394104
30 2.88928896
40 2.50741375
50 2.24550338
60 2.05156084
70 1.90048503
80 1.77844060
90 1.67704721
100 1.59085792
110 1.51614304
120 1.45025226
130 1.39125374
140 1.33771501
150 1.28856146
160 1.24298016
170 1.20035213
180 1.16020337
190 1.12216882
200 1.08596559
210 1.05137298
220 1.01821734
230 0.98636083
240 0.95569280
250 0.92612336
260 0.89757851
270 0.86999638
280 0.84332454
290 0.81751777
300 0.79253655
310 0.76834575
320 0.74491380
330 0.72221190
340 0.70021355
350 0.67889409
360 0.65823042
370 0.63820074
380 0.61878436
390 0.59996158
400 0.58171354
410 0.56402217
420 0.54687008
430 0.53024053
440 0.51411737
450 0.49848501
460 0.48332837
STATUS: SOLUTION OBTAINED AT
TIME LEVEL= 1.08 s.
TIME STEPS= 468
Writing convergence log file: Completed.
Files saved:
"D:/MyProjects/projectroot/nanpack/output/HISTftcs1D.dat".
In the above convergence monitor, it is worth noting that the solution error is gradually moving towards zero which is what we need to confirm stability in the solution. If the solution becomes unstable, the errors will rise, probably upto the point where your code will crash. As you know that the solution obtained is a time-dependent solution and therefore, we didn’t allow the code to run until the convergence is observed. If a steady-state solution is desired, change the STATE key in the configuration file equals to “STEADY” and specify a much larger value of nMax key, say nMax = 5000. This is left as an exercise for the users to obtain a stead-state solution. Also, try running the solution with the larger grid step size, \(\Delta x\) or a larger time step size, \(\Delta t\).
After the time stepping is completed, save the final results to the output files.
[9]:
# Write output to file
post.WriteSolutionToFile(U, n, cfg.nWrite, cfg.nMax,
cfg.OutFileName, cfg.dX)
# Write convergence history log to a file
post.WriteConvHistToFile(cfg, n, Error)
Verify that the files are saved in the target directory. Now let us obtain analytical solution of this flow that will help us in validating our codes.
[10]:
# Obtain analytical solution
Uana = ParallelPlateFlow(40.0, X, cfg.diff, cfg.totTime, 20)
Next, we will validate our results by plotting the results using the matplotlib package that we have imported above. Type the following lines of codes:
[11]:
plt.rc("font", family="serif", size=8) # Assign fonts in the plot
fig, ax = plt.subplots(dpi=150) # Create axis for plotting
plt.plot(U, X, ">-.b", linewidth=0.5, label="FTCS",\
markersize=5, markevery=5) # Plot data with required labels and markers, customize the plot however you may like
plt.plot(Uana, X, "o:r", linewidth=0.5, label="Analytical",\
markersize=5, markevery=5) # Plot analytical solution on the same plot
plt.xlabel('Velocity (m/s)') # X-axis labelling
plt.ylabel('Plate distance (m)') # Y-axis labelling
plt.title(f"Velocity profile\nat t={cfg.totTime} sec", fontsize=8) # Plot title
plt.legend()
plt.show() # Show plot- this command is very important
Function for the boundary conditions.
[1]:
def BC(U):
"""Return the dependent variable with the updated values at the boundaries."""
U[0] = 40.0
U[-1] = 0.0
return U
Congratulations, you have completed the first coding tutoria using nanpack package and verified that your codes produced correct results. If you solve some other similar diffusion-1D model example, share it with the nanpack community. I will be excited to see your projects.