3.5 Computational Strategies in Mathematica
3.5.2 Eigencondition evaluation
A more difficult numerical problem is to evaluate non–explicit eigenconditions, such as those occur- ing for convection–type BCs in cartesian geometries and any SOV solution in cylindrical geometries. Mathematica provides an intrisic rootfinding algorithm, FindRoot, which uses Newton’s method to find a root of a nonlinear equation. This function requires the equation to be solved (say f (x) = 0), the independent variable (x), and the starting point of the variable for the search (x1). This last
piece of information (the starting point) is the most challenging quantity to pin down. Recognize that the eigencondition intrinsically has an infinite number of roots, and to select any one root via FindRootrequires that the search begin at a point relatively ‘close’ to the desired root.
A way to overcome this problem is to find a pair of points, x = xp and xm, so that f (xp) and
f (xm) bisect the desired solution f (x = xR) = 0. This bisection implies that f (xp) · f(xm) ≤ 0
– because one function must be in the region where f ≥ 0 and the other in the region f ≤ 0. Two such points are relatively easy to find; one would begin with two initial points xm = x1 and
xp = xm+ ∆x, where ∆x is a chosen step size for x, and make the test f (xm) · f(xp) ≤ 0. If this is
not the case, then set xm = xp, xp = xp+ ∆x, and reperform the test. Essentially, the algorithm
steps (or marches) x along until a bisection in the function is obtained.
Once the bisection is identified, the root can be approximated by linear interpolation; xR≈ xp−
f (xp)∆x
f (xp) − f(xm)
This approximation can then be given to the Mathematica FindRoot function to finish the job. A Mathematica function which performs this strategy is given below;
eigenroot[lamstart_]:=Module[{dlam = 0.2, lam0,eigen0,eigen1,lamr,lamroot}, lam0=lamstart+1*^-6; eigen0=eigencond[lam0]; lam0=lam0+dlam; eigen1=eigencond[lam0]; While[eigen0 eigen1>0, lam0=lam0+dlam; eigen0=eigen1; eigen1=eigencond[lam0]; ]; lamr=lam0-eigen1 dlam/(eigen1-eigen0); lamroot= lam/.FindRoot[eigencond[lam]==0,{lam,lamr}]; lamroot]
The function returns the first root to the user–defined equation eigencond[lam] that occurs after the point lam=lamstart. It uses a hard–wired step size of ∆λ = 0.2, which is adequately small for all problems encountered in this chapter. The command Module provides a way of coding a ‘subroutine’ in Mathematica. The variable names enclosed in brackets in the first line of Module are the ‘temporary variables’ of the subroutine; assignments to these variables are made only within the subroutine and are not ‘global’. The rest of the subroutine consists of individual statements, with each statement ended by a semicolon except the last one. The quantity returned by Module is the last statement (i.e., the value of lamroot. Refer to Mathematica help for more information on coding with Module.
The algorithm used by the function follows that described above; eigen0 and eigen1 correspond to f (xm) and f (xp), and the first value of xmis set to lamstart + 10−6. The While block continues
the marching process until the bisection point is found. lamr corresponds to the interpolated root from the bisection, and lamroot is the root returned by FindRoot using the interpolated approximation as the starting point.
The addition of the small number 10−6 to lamstart, to derive the first evaluation point, is
included so that lamstart can correspond to a known root; when, for example, lamstart is set at λ1(where λ1is known), the function will return the next root λ2. The first root λ1 will be returned
by setting lamstart=0, providing that the root is larger than 10−6.
The code requires the user to define beforehand the function eigencond[lam], which must return a numerical value of the eigencondition relation for a given numerical value of λ = lam. The eigencondition corresponds to eigencond[lam]==0. This function should be coded to avoid singularities for λ > 0. For example, the plane wall convective BC eigencondition should appear eigencond[lam_]:=lam Sin[lam] -bi Cos[lambda]
as opposed to the equivalent form λ tan(λ) − Bi, which has singularities at λ = (2n − 1)π/2. Recognize that the constant bi in the eigencondition must be assigned a numerical value prior to execution of the functions, e.g., by executing the line bi = 5 for a Bi value of 5. This would apply to any numerical parameters that appear in the eigencondition.
Series evaluation with eigencondition roots
The most simple – and the computationally most expensive (and most stupid) – method to in- corporate the eigencondition roots into series evaluation would be to modify the series summation code via tsoln[x_,t_]:=-2Sum[((-1)^n Cos[lambdan x] E^(-lambdan^2 t)/lambdan /.lambdan->lambda[n], {n,1,20}] lambda[n_]:=eigenroot[lambda[n-1]]/;n>1 lambda[n_]:=eigenroot[0]/;n<=1
It is easy to see that the function lambda[n] returns the nth root of the eigencondition by recur- sively calling the eigenroot function for λn−1, λn−2, . . . λ1 – and the first root is obtained from
eigenroot[0]. The Mathematica convention ‘/;’, following a function definition, represents a con- ditional test; the first definition of lambda is used if n > 1, otherwise the second definition is used. Of course, such a method will be tremendously time–consuming; evaluation of each root requires evaluation of all previous roots. Furthermore, this process would be performed every time the tsolnfunction is called – even though the all the roots may have been found in a previous function evaluation.
An easy way around this problem is to calculate the roots beforehand and store them in an array. The code could now appear as;
lambda[1]=eigenroot[0];
Do[lambda[n]=eigenroot[lambda[n-1]],{n,2,20}]; tsoln[x_,t_]:=-2Sum[((-1)^n Cos[lambdan x]
E^(-lambdan^2 t)/lambdan
/.lambdan->lambda[n], {n,1,20}]
The quantity lambda[n] now denotes an array element which contains the numerical value of the nth root: note that the operator ‘=’ was used to assign values to lambda[1], lambda[2], etc., as opposed to the function definition ‘:=’ used in the previous code.