mathcal{I}_t is well known that the Crank-Nicolson method may give rise to non-physical oscillations in the solution of diffusion equations if the initial data exhibit jumps (see the section diffu:pde1:analysis:CN). Rannacher [Rannacher_1984] suggested a stabilizing technique consisting of using the Backward Euler scheme for the first two time steps with step length $\frac{1}{2}\Delta t$. One can generalize this idea to taking $2m$ time steps of size $\frac{1}{2}\Delta t$ with the Backward Euler method and then continuing with the Crank-Nicolson method, which is of second-order in time. The idea is that the high frequencies of the initial solution are quickly damped out, and the Backward Euler scheme treats these high frequencies correctly. Thereafter, the high frequency content of the solution is gone and the Crank-Nicolson method will do well.
Test this idea for $m=1,2,3$ on a diffusion problem with a
discontinuous initial condition. Measure the convergence rate using
the solution (diffu:analysis:pde1:step:erf:sol) with the boundary
conditions
(diffu:analysis:pde1:p1:erf:uL)-(diffu:analysis:pde1:p1:erf:uR)
for $t$ values such that the conditions are in the vicinity of $\pm 1$.
For example, $t< 5a 1.6\cdot 10^{-2}$ makes the solution diffusion from
a step to almost a straight line. The
program diffu_erf_sol.py
shows how to compute the analytical
solution.
This project concerns so-called energy estimates for diffusion problems that can be used for qualitative analytical insight and for verification of implementations.
a) We start with a 1D homogeneous diffusion equation with zero Dirichlet conditions:
The energy estimate for this problem reads
where the $||\cdot ||_{L^2}$ norm is defined by
The quantify $||u||_{L^2}$ or $\frac{1}{2} ||u||_{L^2}$ is known as the energy of the solution, although it is not the physical energy of the system. A mathematical tradition has introduced the notion energy in this context.
The estimate (4) says that the "size of $u$" never exceeds that of the initial condition, or more precisely, it says that the area under the $u$ curve decreases with time.
To show (4), multiply the PDE by $u$ and integrate from $0$ to $L$. Use that $uu_t$ can be expressed as the time derivative of $u^2$ and that $u_xxu$ can integrated by parts to form an integrand $u_x^2$. Show that the time derivative of $||u||_{L^2}^2$ must be less than or equal to zero. Integrate this expression and derive (4).
b) Now we address a slightly different problem,
The associated energy estimate is
(This result is more difficult to derive.)
Now consider the compound problem with an initial condition $I(x)$ and a right-hand side $f(x,t)$:
c) One application of (13) is to prove uniqueness of the solution. Suppose $u_1$ and $u_2$ both fulfill (10)-(12). Show that $u=u_1 - u_2$ then fulfills (10)-(12) with $f=0$ and $I=0$. Use (13) to deduce that the energy must be zero for all times and therefore that $u_1=u_2$, which proves that the solution is unique.
d) Generalize (13) to a 2D/3D diffusion equation $u_t = \nabla\cdot (\alpha \nabla u)$ for $x\in\Omega$.
Hint. Use integration by parts in multi dimensions:
where $\frac{\partial u}{\partial n} = \boldsymbol{n}\cdot\nabla u$, $\boldsymbol{n}$ being the outward unit normal to the boundary $\partial\Omega$ of the domain $\Omega$.
e) Now we also consider the multi-dimensional PDE $u_t = \nabla\cdot (\alpha \nabla u)$. Integrate both sides over $\Omega$ and use Gauss' divergence theorem, $\int_\Omega \nabla\cdot\boldsymbol{q}\dx = \int_{\partial\Omega}\boldsymbol{q}\cdot\boldsymbol{n}\ds$ for a vector field $\boldsymbol{q}$. Show that if we have homogeneous Neumann conditions on the boundary, $\partial u/\partial n=0$, area under the $u$ surface remains constant in time and
f) Establish a code in 1D, 2D, or 3D that can solve a diffusion equation with a source term $f$, initial condition $I$, and zero Dirichlet or Neumann conditions on the whole boundary.
We can use (13) and (14) as a partial verification of the code. Choose some functions $f$ and $I$ and check that (13) is obeyed at any time when zero Dirichlet conditions are used. mathcal{I}_terate over the same $I$ functions and check that (14) is fulfilled when using zero Neumann conditions.
g) Make a list of some possible bugs in the code, such as indexing errors in arrays, failure to set the correct boundary conditions, evaluation of a term at a wrong time level, and similar. For each of the bugs, see if the verification tests from the previous subexercise pass or fail. This investigation shows how strong the energy estimates and the estimate (14) are for pointing out errors in the implementation.
Filename: diffu_energy
.
In the section diffu:2D:direct_vs_iter, we outlined a class of iterative methods for $Au=b$ based on splitting $A$ into $A=M-N$ and introducing the iteration
The very simplest splitting is $M=I$, where $I$ is the identity matrix. Show that this choice corresponds to the iteration
where $r^{k-1}$ is the residual in the linear system in iteration $k-1$. The formula (15) is known as Richardson's iteration. Show that if we apply the simple iteration method (15) to the preconditioned system $M^{-1}Au=M^{-1}b$, we arrive at the Jacobi method by choosing $M=D$ (the diagonal of $A$) as preconditioner and the SOR method by choosing $M=\omega^{-1}D + L$ ($L$ being the lower triangular part of $A$). This equivalence shows that we can apply one iteration of the Jacobi or SOR method as preconditioner.
Solution. Inserting $M=I$ and $N=I-A$ in the iterative method leads to
which is (15). Replacing $A$ by $M^{-1}A$ and $b$ by $M^{-1}b$ in this equation gives
which we after multiplication by $M$ and reordering can write as
which is the standard form for the Jacobi and SOR methods. Choosing $M=D$ gives Jacobi and $M=\omega^{-1}D+L$ gives SOR. We have shown that we may view $M$ as a preconditioner of a simplest possible iteration method.
Consider a day-and-night or seasonal variation in temperature at the surface of the earth. How deep down in the ground will the surface oscillations reach? For simplicity, we model only the vertical variation along a coordinate $x$, where $x=0$ at the surface, and $x$ increases as we go down in the ground. The temperature is governed by the heat equation
in some spatial domain $x\in [0,L]$, where $L$ is chosen large enough such that we can assume that $T$ is approximately constant, independent of the surface oscillations, for $x>L$. The parameters $\varrho$, $c_v$, and $k$ are the density, the specific heat capacity at constant volume, and the heat conduction coefficient, respectively.
a) Derive the mathematical model for computing $T(x,t)$. Assume the surface oscillations to be sinusoidal around some mean temperature $T_m$. Let $T=T_m$ initially. At $x=L$, assume $T\approx T_m$.
Solution. The surface temperature is set as
With only one "active" spatial coordinate we get the initial-boundary value problem
b) Scale the model in a) assuming $k$ is constant. Use a time scale $t_c = \omega^{-1}$ and a length scale $x_c = \sqrt{2\dfc/\omega}$, where $\dfc = k/(\varrho c_v)$. The primary unknown can be scaled as $\frac{T-T_m}{2A}$.
Show that the scaled PDE is
with initial condition $u(\bar x,0) = 0$, left boundary condition $u(0,\bar t) = \sin(\bar t)$, and right boundary condition $u(\bar L,\bar t) = 0$. The bar indicates a dimensionless quantity.
Show that $u(\bar x, \bar t)=e^{-\bar x}\sin (\bar x - \bar t)$ is a solution that fulfills the PDE and the boundary condition at $\bar x =0$ (this is the solution we will experience as $\bar t\rightarrow\infty$ and $L\rightarrow\infty$). Conclude that an appropriate domain for $x$ is $[0,4]$ if a damping $e^{-4}\approx 0.18$ is appropriate for implementing $\bar u\approx\hbox{const}$; increasing to $[0,6]$ damps $\bar u$ to 0.0025.
Solution. Chapter 3.2.4 in the book [Langtangen_scaling] describes the scaling of this problem in detail. Inserting dimensionless variables $\bar t = \omega t$, $\bar x = \sqrt{\omega/(2\dfc)} x$, and
leads to
The domain lengths $\bar L$ and $\bar T$ follows from straightforward scaling of $L$ and $T$.
Inserting $u(\bar x, \bar t)=e^{-\bar x}\sin (\bar t - \bar x)$ in the PDE shows that this is a solution. mathcal{I}_t also obeys the boundary condition $\bar u(0,\bar t)=sin(\bar t)$. As $\bar t\rightarrow\infty$, the initial condition has no longer impact on the solution and is "forgotten" and of no interest. The boundary condition at $\bar x=\bar L$ is never compatible with the given solution unless $\bar u$ is damped to zero, which happens mathematically as $\bar L\rightarrow\infty$. For a numerical solution, however, we may use a small finite value such as $\bar L=4$.
c) Compute the scaled temperature and make animations comparing two solutions with $\bar L=4$ and $\bar L=8$, respectively (keep $\Delta x$ the same).
Solution.
We can use the viz
function in diff1D_vc.py
to do the number
crunching. Appropriate calls and visualization go here:
%matplotlib inline
import sys, os
sys.path.insert(0, os.path.join(os.pardir, 'src-diffu'))
from diffu1D_vc import viz
sol = [] # store solutions
for Nx, L in [[20, 4], [40, 8]]:
dt = 0.1
dx = float(L)/Nx
D = dt/dx**2
from math import pi, sin
T = 2*pi*6
from numpy import zeros
a = zeros(Nx+1) + 0.5
cpu, u_ = viz(
I=lambda x: 0, a=a, L=L, Nx=Nx, D=D, T=T,
umin=-1.1, umax=1.1, theta=0.5,
u_L=lambda t: sin(t),
u_R=0,
animate=False, store_u=True)
sol.append(u_)
print('computed solution for Nx=%d in [0,%g]' % (Nx, L))
print sol[0].shape
print sol[1].shape
import scitools.std as plt
counter = 0
for u0, u1 in zip(sol[0][2:], sol[1][2:]):
x0 = sol[0][0]
x1 = sol[1][0]
plt.plot(x0, u0, 'r-', x1, u1, 'b-',
legend=['short', 'long'],
savefig='tmp_%04d.png' % counter,
axis=[x1[0], x1[-1], -1.1, 1.1])
counter += 1
from IPython.display import HTML
_s = """
<div>
<video loop controls width='640' height='365' preload='none'>
<source src='https://github.com/hplgit/fdm-book/raw/master/doc/pub/book/html/mov-diffu/surface_osc/movie.mp4' type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'>
<source src='https://github.com/hplgit/fdm-book/raw/master/doc/pub/book/html/mov-diffu/surface_osc/movie.webm' type='video/webm; codecs="vp8, vorbis"'>
<source src='https://github.com/hplgit/fdm-book/raw/master/doc/pub/book/html/mov-diffu/surface_osc/movie.ogg' type='video/ogg; codecs="theora, vorbis"'>
</video>
</div>
<p><em></em></p>
<!-- Issue warning if in a Safari browser -->
<script language="javascript">
if (!!(window.safari)) {
document.write("<div style=\"width: 95%%; padding: 10px; border: 1px solid #100; border-radius: 4px;\"><p><font color=\"red\">The above movie will not play in Safari - use Chrome, Firefox, or Opera.</font></p></div>")}
</script>
"""
HTML(_s)
We consider flow in a straight tube with radius $R$ and straight walls. The flow is driven by a pressure gradient $\beta(t)$. The effect of gravity can be neglected. The mathematical problem reads
We consider two models for $\beta(t)$. One plain, sinusoidal oscillation:
and one with periodic pulses,
Note that both models can be written as $\beta = A\sin^m(\omega t)$, with $m=1$ and $m=16$, respectively.
a) Scale the mathematical model, using the viscous time scale $\varrho R^2/\mu$.
Solution. We can introduce
Inserted in the PDE, we get
where $\alpha$ is a dimensionless number
reflecting the ratio of the viscous diffusion time scale and the time scale of the oscillating pressure gradient. We may choose $u_c$ such that the coefficient in the pressure gradient term equals unity:
The governing PDE, dropping the bars, then reads
b) Implement the scaled model from a), using the unifying $\theta$ scheme in time and centered differences in space.
Solution. We need to take into account extensions below: a coefficient in front of the viscous term, and an extra source term.
A preliminary and unfinished code:
"""
Solve the diffusion equation for axi-symmetric case:
u_t = 1/r * (r*a(r)*u_r)_r + f(r,t)
on (0,R) with boundary conditions u(0,t)_r = 0 and u(R,t) = 0,
for t in (0,T]. Initial condition: u(r,0) = I(r).
Pressure gradient f.
The following naming convention of variables are used.
===== ==========================================================
Name Description
===== ==========================================================
Nx The total number of mesh cells; mesh points are numbered
from 0 to Nx.
T The stop time for the simulation.
I Initial condition (Python function of x).
a Variable coefficient (constant).
R Length of the domain ([0,R]).
r Mesh points in space.
t Mesh points in time.
n Index counter in time.
u Unknown at current/new time level.
u_1 u at the previous time level.
dr Constant mesh spacing in r.
dt Constant mesh spacing in t.
===== ==========================================================
``user_action`` is a function of ``(u, r, t, n)``, ``u[i]`` is the
solution at spatial mesh point ``r[i]`` at time ``t[n]``, where the
calling code can add visualization, error computations, data analysis,
store solutions, etc.
"""
import scipy.sparse
import scipy.sparse.linalg
from numpy import linspace, zeros, random, array, ones, sum, log, sqrt
import time, sys
import sympy as sym
def solver_theta(I, a, R, Nr, D, T, theta=0.5, u_L=None, u_R=0,
user_action=None, f=0):
"""
The array a has length Nr+1 and holds the values of
a(x) at the mesh points.
Method: (implicit) theta-rule in time.
Nr is the total number of mesh cells; mesh points are numbered
from 0 to Nr.
D = dt/dr**2 and implicitly specifies the time step.
T is the stop time for the simulation.
I is a function of r.
u_L = None implies du/dr = 0, i.e. a symmetry condition
f(r,t) is pressure gradient with radius.
user_action is a function of (u, x, t, n) where the calling code
can add visualization, error computations, data analysis,
store solutions, etc.
r*alpha is needed midway between spatial mesh points, - use
arithmetic mean of successive mesh values (i.e. of r_i*alpha_i)
"""
import time
t0 = time.perf_counter()
r = linspace(0, R, Nr+1) # mesh points in space
dr = r[1] - r[0]
dt = D*dr**2
Nt = int(round(T/float(dt)))
t = linspace(0, T, Nt+1) # mesh points in time
if isinstance(u_L, (float,int)):
u_L_ = float(u_L) # must take copy of u_L number
u_L = lambda t: u_L_
if isinstance(u_R, (float,int)):
u_R_ = float(u_R) # must take copy of u_R number
u_R = lambda t: u_R_
if isinstance(f, (float,int)):
f_ = float(f) # must take copy of f number
f = lambda r, t: f_
ra = r*a # help array in scheme
inv_r = zeros(len(r)-2) # needed for inner mesh points
inv_r = 1.0/r[1:-1]
u = zeros(Nr+1) # solution array at t[n+1]
u_1 = zeros(Nr+1) # solution at t[n]
Dl = 0.5*D*theta
Dr = 0.5*D*(1-theta)
# Representation of sparse matrix and right-hand side
diagonal = zeros(Nr+1)
lower = zeros(Nr)
upper = zeros(Nr)
b = zeros(Nr+1)
# Precompute sparse matrix (scipy format)
diagonal[1:-1] = 1 + Dl*(ra[2:] + 2*ra[1:-1] + ra[:-2])*inv_r
lower[:-1] = -Dl*(ra[1:-1] + ra[:-2])*inv_r
upper[1:] = -Dl*(ra[2:] + ra[1:-1])*inv_r
# Insert boundary conditions
if u_L == None: # symmetry axis, du/dr = 0
diagonal[0] = 1 + 8*a[0]*Dl
upper[0] = -8*a[0]*Dl
else:
diagonal[0] = 1
upper[0] = 0
diagonal[Nr] = 1
lower[-1] = 0
A = scipy.sparse.diags(
diagonals=[diagonal, lower, upper],
offsets=[0, -1, 1],
shape=(Nr+1, Nr+1),
format='csr')
#print A.todense()
# Set initial condition
for i in range(0,Nr+1):
u_1[i] = I(r[i])
if user_action is not None:
user_action(u_1, r, t, 0)
# Time loop
for n in range(0, Nt):
b[1:-1] = u_1[1:-1] + Dr*(
(ra[2:] + ra[1:-1])*(u_1[2:] - u_1[1:-1]) -
(ra[1:-1] + ra[0:-2])*(u_1[1:-1] - u_1[:-2]))*inv_r + \
dt*theta*f(r[1:-1], t[n+1]) + \
dt*(1-theta)*f(r[1:-1], t[n])
# Boundary conditions
if u_L == None: # symmetry axis, du/dr = 0
b[0] = u_1[0] + 8*a[0]*Dr*(u_1[1] - u_1[0]) + \
dt*theta*f(0, (n+1)*dt) + \
dt*(1 - theta)*f(0, n*dt)
else:
b[0] = u_L(t[n+1])
b[-1] = u_R(t[n+1])
#print b
# Solve
u[:] = scipy.sparse.linalg.spsolve(A, b)
if user_action is not None:
user_action(u, r, t, n+1)
# Switch variables before next step
u_1, u = u, u_1
t1 = time.perf_counter()
# return u_1, since u and u_1 are switched
return u_1, t, t1-t0
def compute_rates(h_values, E_values):
m = len(h_values)
q = [log(E_values[i+1]/E_values[i])/
log(h_values[i+1]/h_values[i])
for i in range(0, m-1, 1)]
q = [round(q_, 2) for q_ in q]
return q
def make_a(alpha, r):
"""
alpha is a func, generally of r, - but may be constant.
Note: when solution is to be axi-symmetric, alpha
must be so too.
"""
a = alpha(r)*ones(len(r))
return a
def tests_with_alpha_and_u_exact():
'''
Test solver performance when alpha is either const or
a fu of r, combined with a manufactured sol u_exact
that is either a fu of r only, or a fu of both r and t.
Note: alpha and u_e are defined as symb expr here, since
test_solver_symmetric needs to automatically generate
the source term f. After that, test_solver_symmetric
redefines alpha, u_e and f as num functions.
'''
R, r, t = sym.symbols('R r t')
# alpha const ...
# ue = const
print('Testing with alpha = 1.5 and u_e = R**2 - r**2...')
test_solver_symmetric(alpha=1.5, u_exact=R**2 - r**2)
# ue = ue(t)
print('Testing with alpha = 1.5 and u_e = 5*t*(R**2 - r**2)...')
test_solver_symmetric(alpha=1.5, u_exact=5*t*(R**2 - r**2))
# alpha function of r ...
# ue = const
print('Testing with alpha = 1 + r**2 and u_e = R**2 - r**2...')
test_solver_symmetric(alpha=1+r**2, u_exact=R**2 - r**2)
# ue = ue(t)
print('Testing with alpha = 1+r**2 and u_e = 5*t*(R**2 - r**2)...')
test_solver_symmetric(alpha=1+r**2, u_exact=5*t*(R**2 - r**2))
def test_solver_symmetric(alpha, u_exact):
'''
Test solver performance for manufactured solution
given in the function u_exact. Parameter alpha is
either a const or a function of r. In the latter
case, an "exact" sol can not be achieved, so then
testing switches to conv. rates.
R is tube radius and T is duration of simulation.
alpha constant:
Compares the manufactured solution with the
solution from the solver at each time step.
alpha function of r:
convergence rates are tested (using the sol
at the final point in time only).
'''
def compare(u, r, t, n): # user_action function
"""Compare exact and computed solution."""
u_e = u_exact(r, t[n])
diff = abs(u_e - u).max()
#print diff
tol = 1E-12
assert diff < tol, 'max diff: %g' % diff
def pde_source_term(a, u):
'''Return the terms in the PDE that the source term
must balance, here du/dt - (1/r) * d/dr(r*a*du/dr).
a, i.e. alpha, is either const or a fu of r.
u is a symbolic Python function of r and t.'''
return sym.diff(u, t) - \
(1.0/r)*sym.diff(r*a*sym.diff(u, r), r)
R, r, t = sym.symbols('R r t')
# fit source term
f = sym.simplify(pde_source_term(alpha, u_exact))
R = 1.0 # radius of tube
T = 2.0 # duration of simulation
if sym.diff(alpha, r) == 0:
alpha_is_const = True
else:
alpha_is_const = False
# make alpha, f and u_exact numerical functions
alpha = sym.lambdify([r], alpha, modules='numpy')
f = sym.lambdify([r, t], f.subs('R', R), modules='numpy')
u_exact = sym.lambdify(
[r, t], u_exact.subs('R', R), modules='numpy')
I = lambda r: u_exact(r, 0)
# some help variables
FE = 0 # Forward Euler method
BE = 1 # Backward Euler method
CN = 0.5 # Crank-Nicolson method
# test all three schemes
for theta in (FE, BE, CN):
print('theta: ', theta)
E_values = []
dt_values = []
for Nr in (2, 4, 8, 16, 32, 64):
print('Nr:', Nr)
r = linspace(0, R, Nr+1) # mesh points in space
dr = r[1] - r[0]
a_values = make_a(alpha, r)
if theta == CN:
dt = dr
else: # either FE or BE
# use most conservative dt as decided by FE
K = 1.0/(4*a_values.max())
dt = K*dr**2
D = dt/dr**2
if alpha_is_const:
u, t, cpu = solver_theta(
I, a_values, R, Nr, D, T,
theta, u_L=None, u_R=0,
user_action=compare, f=f)
else: # alpha depends on r
u, t, cpu = solver_theta(
I, a_values, R, Nr, D, T,
theta, u_L=None, u_R=0,
user_action=None, f=f)
# compute L2 error at t = T
u_e = u_exact(r, t[-1])
e = u_e - u
E = sqrt(dr*sum(e**2))
E_values.append(E)
dt_values.append(dt)
if alpha_is_const is False:
q = compute_rates(dt_values, E_values)
print('theta=%g, q: %s' % (theta, q))
expected_rate = 2 if theta == CN else 1
tol = 0.1
diff = abs(expected_rate - q[-1])
print('diff:', diff)
assert diff < tol
if __name__ == '__main__':
tests_with_alpha_and_u_exact()
print('This is just a start. More remaining for this Exerc.')
c) Verify the implementation in b) using a manufactured solution that is quadratic in $r$ and linear in $t$. Make a corresponding test function.
Hint. You need to include an extra source term in the equation to allow for such tests. Let the spatial variation be $1-r^2$ such that the boundary condition is fulfilled.
d) Make animations for $m=1,16$ and $\alpha=1,0.1$. Choose $T$ such that the motion has reached a steady state (non-visible changes from period to period in $u$).
e) For $\alpha\gg 1$, the scaling in a) is not good, because the characteristic time for changes (due to the pressure) is much smaller than the viscous diffusion time scale ($\alpha$ becomes large). We should in this case base the short time scale on $1/\omega$. Scale the model again, and make an animation for $m=1,16$ and $\alpha = 10$.
Solution. Now the governing PDE becomes
In this case,
We see that for $\alpha\gg 1$, we can neglect the viscous term, and we basically have a balance between the acceleration and the driving pressure gradient:
[hpl 1: This may be a great challenge numerically, since we have a plug independent of r that oscillates back and forth. CN is probably very unstable. Can make a point out of this. Try $\alpha=1$ and increase gently.]
Filename: axisymm_flow
.
Welding equipment makes a very localized heat source that moves in time. We shall investigate the heating due to welding and choose, for maximum simplicity, a one-dimensional heat equation with a fixed temperature at the ends, and we neglect melting. We shall scale the problem, and besides solving such a problem numerically, the aim is to investigate the appropriateness of alternative scalings.
The governing PDE problem reads
Here, $u$ is the temperature, $\varrho$ the density of the material, $c$ a heat capacity, $k$ the heat conduction coefficient, $f$ is the heat source from the welding equipment, and $U_s$ is the initial constant (room) temperature in the material.
A possible model for the heat source is a moving Gaussian function:
where $A$ is the strength, $\sigma$ is a parameter governing how peak-shaped (or localized in space) the heat source is, and $v$ is the velocity (in positive $x$ direction) of the source.
a) Let $x_c$, $t_c$, $u_c$, and $f_c$ be scales, i.e., characteristic sizes, of $x$, $t$, $u$, and $f$, respectively. The natural choice of $x_c$ and $f_c$ is $L$ and $A$, since these make the scaled $x$ and $f$ in the interval $[0,1]$. If each of the three terms in the PDE are equally important, we can find $t_c$ and $u_c$ by demanding that the coefficients in the scaled PDE are all equal to unity. Perform this scaling. Use scaled quantities in the arguments for the exponential function in $f$ too and show that
where $\beta$ and $\gamma$ are dimensionless numbers. Give an interpretation of $\beta$ and $\gamma$.
Solution. We introduce
Inserted in the PDE and dividing by $\varrho c u_c/t_c$ such that the coefficient in front of $\partial\bar u/\partial\bar t$ becomes unity, and thereby all terms become dimensionless, we get
Demanding that all three terms are equally important, it follows that
These constraints imply the diffusion time scale
and a scale for $u_c$,
The scaled PDE reads
Scaling $f$ results in
where $\beta$ and $\gamma$ are dimensionless numbers:
The $\sigma$ parameter measures the width of the Gaussian peak, so $\beta$ is the ratio of the domain and the width of the heat source (large $\beta$ implies a very peak-formed heat source). The $\gamma$ parameter arises from $t_c/(L/v)$, which is the ratio of the diffusion time scale and the time it takes for the heat source to travel through the domain. Equivalently, we can multiply by $t_c/t_c$ to get $\gamma = v/(t_cL)$ as the ratio between the velocity of the heat source and the diffusion velocity.
b) Argue that for large $\gamma$ we should base the time scale on the movement of the heat source. Show that this gives rise to the scaled PDE
and
Discuss when the scalings in a) and b) are appropriate.
Solution. We perform the scaling as in a), but this time we determine $t_c$ such that the heat source moves with unit velocity. This means that
Scaling of the PDE gives, as before,
Inserting the expression for $t_c$, we have
We recognize the first coefficient as $\gamma^{-1}$, while $u_c$ can be determined from demanding the second coefficient to be unity:
The scaled PDE is therefore
If the heat source moves very fast, there is little time for the diffusion to transport the heat away from the source, and the heat conduction term becomes insignificant. This is reflected in the coefficient $\gamma^{-1}$, which is small when $\gamma$, the ratio of the heat source velocity and the diffusion velocity, is large.
The scaling in a) is therefore appropriate if diffusion is a significant process, i.e., the welding equipment moves at a slow speed so heat can efficiently spread out by diffusion. For large $\gamma$, the scaling in b) is appropriate, and $t=1$ corresponds to having the heat source traveled through the domain (with the scaling in a), the heat source will leave the domain in short time).
c) One aim with scaling is to get a solution that lies in the interval $[-1,1]$. This is not always the case when $u_c$ is based on a scale involving a source term, as we do in a) and b). However, from the scaled PDE we realize that if we replace $\bar f$ with $\delta\bar f$, where $\delta$ is a dimensionless factor, this corresponds to replacing $u_c$ by $u_c/\delta$. So, if we observe that $\bar u\sim1/\delta$ in simulations, we can just replace $\bar f$ by $\delta \bar f$ in the scaled PDE.
Use this trick and implement the two scaled models. Reuse software for
the diffusion equation (e.g., the solver
function in
diffu1D_vc.py
). Make a function run(gamma, beta=10, delta=40, scaling=1, animate=False)
that runs the model with the given
$\gamma$, $\beta$, and $\delta$ parameters as well as an indicator
scaling
that is 1 for the scaling in a) and 2 for the scaling in
b). The last argument can be used to turn screen animations on or off.
Experiments show that with $\gamma=1$ and $\beta=10$, $\delta =20$ is appropriate. Then $\max |\bar u|$ will be larger than 4 for $\gamma =40$, but that is acceptable.
Equip the run
function with visualization, both animation of $\bar u$
and $\bar f$, and plots with $\bar u$ and $\bar f$ for $t=0.2$ and $t=0.5$.
Hint. Since the amplitudes of $\bar u$ and $\bar f$ differs by a factor $\delta$, it is attractive to plot $\bar f/\delta$ together with $\bar u$.
Solution.
Here is a possible run
function:
# from .diffu1D_vc import solver
import numpy as np
def run(gamma, beta=10, delta=40, scaling=1, animate=False):
"""Run the scaled model for welding."""
if scaling == 1:
v = gamma
a = 1
elif scaling == 2:
v = 1
a = 1.0/gamma
b = 0.5*beta**2
L = 1.0
ymin = 0
# Need gloal to be able change ymax in closure process_u
global ymax
ymax = 1.2
I = lambda x: 0
f = lambda x, t: delta*np.exp(-b*(x - v*t)**2)
import time
import scitools.std as plt
plot_arrays = []
def process_u(u, x, t, n):
global ymax
if animate:
plt.plot(x, u, 'r-',
x, f(x, t[n])/delta, 'b-',
axis=[0, L, ymin, ymax], title='t=%f' % t[n],
xlabel='x', ylabel='u and f/%g' % delta)
if t[n] == 0:
time.sleep(1)
plot_arrays.append(x)
dt = t[1] - t[0]
tol = dt/10.0
if abs(t[n] - 0.2) < tol or abs(t[n] - 0.5) < tol:
plot_arrays.append((u.copy(), f(x, t[n])/delta))
if u.max() > ymax:
ymax = u.max()
Nx = 100
D = 10
T = 0.5
u_L = u_R = 0
theta = 1.0
cpu = solver(
I, a, f, L, Nx, D, T, theta, u_L, u_R, user_action=process_u)
x = plot_arrays[0]
plt.figure()
for u, f in plot_arrays[1:]:
plt.plot(x, u, 'r-', x, f, 'b--', axis=[x[0], x[-1], 0, ymax],
xlabel='$x$', ylabel=r'$u, \ f/%g$' % delta)
plt.hold('on')
plt.legend(['$u,\\ t=0.2$', '$f/%g,\\ t=0.2$' % delta,
'$u,\\ t=0.5$', '$f/%g,\\ t=0.5$' % delta])
filename = 'tmp1_gamma%g_s%d' % (gamma, scaling)
s = 'diffusion' if scaling == 1 else 'source'
plt.title(r'$\beta = %g,\ \gamma = %g,\ $' % (beta, gamma)
+ 'scaling=%s' % s)
plt.savefig(filename + '.pdf'); plt.savefig(filename + '.png')
return cpu
Note that we have dropped the bar notation in the plots. mathcal{I}_t is common to drop the bars as soon as the scaled problem is established.
d) Use the software in c) to investigate $\gamma=0.2,1,5,40$ for the two scalings. Discuss the results.
Solution. For these investigations, we compare the two scalings for each of the different $\gamma$ values. An appropriate function for automating the tasks is
def investigate():
"""Do scienfic experiments with the run function above."""
# Clean up old files
import glob
for filename in glob.glob('tmp1_gamma*') + \
glob.glob('welding_gamma*'):
os.remove(filename)
gamma_values = 1, 40, 5, 0.2, 0.025
for gamma in gamma_values:
for scaling in 1, 2:
run(gamma=gamma, beta=10, delta=20, scaling=scaling)
# Combine images
for gamma in gamma_values:
for ext in 'pdf', 'png':
cmd = 'doconce combine_images -2 '\
'tmp1_gamma%(gamma)g_s1.%(ext)s '\
'tmp1_gamma%(gamma)g_s2.%(ext)s '\
'welding_gamma%(gamma)g.%(ext)s' % vars()
os.system(cmd)
# pdflatex doesn't like 0.2 in filenames...
if '.' in str(gamma):
os.rename(
'welding_gamma%(gamma)g.%(ext)s' % vars(),
('welding_gamma%(gamma)g' % vars()).replace('.', '_')
+ '.' + ext)
We run here a Backward Euler scheme with $N_x=100$ and quite long time steps.
Running the investigate
function, we get the following plots:
For $\gamma\ll 1$ as in $\gamma = 0.025$, the heat source moves very slowly on the diffusion time scale and has hardly entered the medium, while the scaling in b) is not inappropriate, but a larger $\delta$ is needed to bring $\bar u$ around unity. We see that for $\gamma=0.2$, each of the scalings work, but with the diffusion time scale, the heat source has not moved much into the domain. For $\gamma=1$, the mathematical problems are identical and hence the plots too. For $\gamma=5$, the time scale based on the source is clearly the best choice, and for $\gamma=40$, only this scale is appropriate.
A conclusion is that the scaling in b) works well for a range of $\gamma$ values, also in the case $\gamma\ll 1$.
Filename: welding
.
Based on the discussion in the section diffu:fd2:radial, derive in detail the discrete equations for a Forward Euler in time, centered in space, finite difference method for axi-symmetric diffusion. The diffusion coefficient may be a function of the radial coordinate. At the outer boundary $r=R$, we may have either a Dirichlet or Robin condition. Implement this scheme. Construct appropriate test problems.
Solution. We start with the equation at $r=0$. According to the section diffu:fd2:radial, we get
For $i>0$, we have
Solving with respect to $u^{n+1}_i$ and introducing $D=\Delta t/\Delta r^2$ results in
and $u^{n+1}_i$ at the end point $i=N_r$ is assumed known in case of a Dirichlet condition. A Robin condition
can be discretized at $i=N_r$ by
Solving with respect to the value at the fictitious point $i+1$ gives
This value is then inserted for $u_{i+1}^n$ in the discrete PDE at $i=N_r$.
Filename: FE_axisym
.