![]() |
Computational Science Education Outreach and Training (EOT)
|
|
APAC-ANU Teaching Module Graeme Chandler and Stephen Roberts Contents1 Introduction to SCILAB1.1 Introduction 1.2 Login 1.3 Windows Login 1.4 Mac Login 1.5 Talking between SCILAB and the Editor 1.6 Basic Commands 1.7 Matrix Vector problems 1.8 Loops and Conditionals 1.9 The Help Command 1.10 The Help Window 1.11 Summary 1.12 Exercises 2 Matrix Calculations 2.1 Introduction 2.2 Solving Equations 2.3 Matrices and Vectors 2.4 Creating Matrices 2.5 Systems of Equations 3 Data and Function Plots 3.1 Introduction 3.2 Plotting Lines and Data 3.3 Plot data as points 3.4 Adding a Line 3.5 Hints for Good Graphs 3.6 Choose a good scale 3.7 Graphs 3.8 Function Plotting 3.9 Component Arithmetic 3.10 Printing Graphs 3.11 Saving Graphs 4 Polynomials and Interpolation 4.1 Introduction 4.2 Evaluation of Polynomials 4.3 Polynomials 4.4 Interpolating Runge's Function 4.5 Taylor Polynomials 4.6 Chebyshev Interpolation 4.7 Spline Interpolation 4.8 Monotonic Cubic Interpolation 5 Sparse Matrices and Direct Solvers 5.1 Introduction 5.2 Sparse Matrices 5.3 Convert Full to Sparse Matrices 5.4 Sparse Matrix Creation 5.5 Matrix Graph Example 5.6 Sparse Matrix Operations 5.7 Solving Sparse Matrix Equations 5.8 Extensions 6 Iterative Methods Applied to Membrane Problem 6.1 Introduction 6.2 Setup Matrix 6.3 Setting Boundary Values 6.4 Applying Tension 6.5 Solving the Problem 6.6 Faster Creation of Matrix 6.7 Conjugate Gradient Method 6.8 Faster Matrix Vector Multiplication 6.9 Conclusion 7 QR Factorisation and Least Squares 7.1 Introduction 7.2 LU method 7.3 QR Factorisation 7.4 Conditioning 7.5 Solving Equations using Decompositions 8 Solving Initial Value Differential Equations 8.1 Introduction 8.2 Scalar Ordinary Differential Equation (ODE's) 9 Chemical Reaction Example 9.1 Chemical Reaction Systems 9.2 Robertson's Example 10 Boundary Value Problems 10.1 Introduction 10.2 Plotting 10.3 Equation Solver 10.4 Two Point Boundary Problem 10.5 Exercises 11 Heat and Wave Equations 11.1 Introduction 11.2 Heat Equation 11.3 Stability of the Method 11.4 Wave Equation 11.5 Experimentation 11.6 Stability 11.7 Exercises
Chapter 1
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
A = [ 1 3 4 ; -1 2 5 ; 4 -3 5 ]The semi-colons may be replaced by returns. SCILAB is case sensitive so that for example a and A will represent different variables. All the built-in functions in SCILAB like the rank or the eigenvalues for a matrix, are denoted by lower-case names, as in rank(A) or spec(A). If you cannot remember how to use a particular SCILAB command try the help command in SCILAB. Try
help specSCILAB provides many vector and matrix operations. For instance we can multiply matrices and vectors. Try the simple commands
A*x A*A
format(20,'e')Try some calculations to see the different format. The format can be changed back using
format(10,'v')A semi-colon at the end of a line will stop SCILAB output for that line. This is useful if you are working with large vectors or matrices. Try
y = A*x ;Note that the answer is not printed out. To see y just type
yLet's make some larger vectors. It is easy to produce vectors that have components which increment nicely. Try
w = 0:0.1:7This will produce a vector starting at 0 and increasing by 0.1 until 7 is reached. Note that a semi-colon at the end of the line would be helpful here. We can take functions of vectors (or matrices). For instance try
z = sin(w)Now we have two vectors w and z of the same length. We can plot the values of w versus the values of z. Try
plot2d(w,z)A plot of w versus z should be produced. You can save the graph to a file by using the file - export menu item on the graphics window.
y = A \ xThe vector y should solve the linear equation x = A*y (check this). The inverse of a matrix can also be calculated using the inv command. Use the inv command to solve the matrix equation A y = x.
ans = 0; n = 1; term = 1;
while( ans + term ~= ans )
ans = ans + term;
term = term*x/n;
n = n + 1;
end
ans
Here ~= means "not equal to".
We can avoid retyping by saving the program in a file. Open up a
text editor. Type in the program. Save to a file using the file
menu. You will be prompted for a file name. Use the name ex.sci,
the .sci is a standard extension for SCILAB function
files. Go back to the SCILAB window and test your program
ex. Type in a value for x and then issue the command
exec('ex.sci'). For instance try
x = 1.0
exec('ex.sci')
to let SCILAB run your program with x = 1.0.
function y = ex(x)
// EX A simple function to calculate exp(x)
y = 0; n = 1; term = 1;
while( y + term ~= y )
y = y + term;
term = term*x/n;
n = n + 1;
end
endfunction
The lines starting with // are comment lines.
Save your changes. Now you can use the command ex(x). But
you must load in your changes. Try
exec('ex.sci')
ex(1.0)
Alternatively if you are using the editor supplied with SCILAB
you can just use the menu item "execute" to load in your file.
This algorithm is useless for large negative values of x. On
the other hand, as in the lectures we can use this algorithm as a
basis for a more robust algorithm. First though we need to become
familiar with the if statement. The SCILAB if
statement has the simple form
if expression then Scilab statements else Scilab statements endBy the way, SCILAB has a for statement of the form
for i=v Scilab statements endHere i is a dummy variable to be used in the loop, and v is a vector, usually a range of numbers defined 1:20, that the dummy variable will iterate through. So
for j=-4:2:6 disp(j**2) endwill print out the squares of the even numbers from -4 to 6. Use your function ex and the if statement to produce a new function newex which produces reasonable approximations of e^x for x positive or negative (as demonstrated in the lectures). You can use the same file to define both the ex and the newex functions, but you will have to use exec again to load in the new function newex.
help sin // Information about sin.
help + // Gives links to help on Scilab operator names
help log // This is enough information about log
// to show log means log to the base e .
Note that often the help information provides an example of how
the command is used. This can be particularly useful when
experimenting with a new command. You can use cutting and pasting
to run the example commands.
In Scilab the apropos command can also be used to search
for relevant information.
apropos logarithm // Provides a list of
// functions related to logarithms
|
quadroots(1,3,2)for the case of x2 + 3x + 2 = 0. This program can be made to return both roots in a vector by calling them, for example, root1 and root2 in the function file and starting the function definition with the line
function [root1, root2] = quadroots(a,b,c)These can then be stored into two variables such as r1 and r2 by running the function with the command
[r1 r2] = quadroots(1,3,2)The program should avoid division by zero, printing an appropriate message in that case. Make your own decision about what to do in the case of complex roots, since SCILAB can compute and print complex values. Test quadroots on the following examples, checking the results in each case, and pay particular attention to the last example.
//--------------------------------------------------
//
// Test program for the quadroots function
//
// Test a range of inputs.
// Look at the expected results in the table
// to see how to code your function
//--------------------------------------------------
function test_quadroots(quadroots)
n=0
table = zeros(1,5);
//-------------------------------------
// Setup tests, a,b,c r1,r2 in a table
// r1 and r2 are expected roots. We are using
// %nan for non real roots
//-------------------------------------
// x^2 + 1 = 0
n=n+1; table(n,:) = [ 1.0, 0.0, 1.0 %nan, %nan]
// 0x^2 + 2x + 1 = 0
n=n+1; table(n,:) = [ 0.0, 2.0, 1.0, -0.5, %nan]
// x^2 + 3x + 2 = 0
n=n+1; table(n,:) = [ 1.0, 3.0, 2.0, -1.0, -2.0]
// 4x^2 + 24x + 36 = 0
n=n+1; table(n,:) = [ 4.0, 24.0, 36.0, -3.0, -3.0]
//10^18 x^2 - x - 1 = 0
n=n+1; table(n,:) = [ 1.0e18, -1.0, -1.0, 1.0000000005e-9, -9.999999995e-10]
//10^-18 x^2 - x + 1 = 0 (The roots are approximately 1 + 10e-18 and 10^18 - 1))
n=n+1; table(n,:) = [ 1.0e-18, -1.0, 1.0, 1.0e18, 1.0]
//--------------------------------------
// Test each problem.
//--------------------------------------
n = size(table,1)
success = 0.0
for i=1:n,
success = success + ...
print_test(table(i,1),table(i,2),table(i,3),table(i,4),table(i,5),quadroots)
end
printf('================================================\n')
printf('%g successful tests out of %g\n',success,n)
endfunction
//---------------------------------------------
// Print whether specific quadtest is correct.
// Returns 1.0 if correct, 0.0 otherwise
//---------------------------------------------
function success = print_test(a,b,c,r1,r2,quadroot)
success = 0.0
printf('================================================\n')
printf('TEST: %g x^2 + %g x + %g = 0\n',a,b,c)
printf('Expect r1 = %20.15e, r2 = %20.15e \n',r1,r2)
[c1,c2]= quadroots(a,b,c)
printf('Obtained r1 = %20.15e, r2 = %20.15e\n',c1,c2)
if( (isnan(r1) & isnan(c1)) & (isnan(r2) & isnan(c1))) then
printf('TEST PASSED\n')
success = 1.0
return
end
if( (isnan(r2) & isnan(c2)) & close(r1,c1) ) then
printf('TEST PASSED\n')
success = 1.0
return
end
if (close(r1,c1)&close(r2,c2)) then
printf('TEST PASSED\n')
success = 1.0
return
end
if (close(r1,c2)&close(r2,c1)) then
printf('TEST PASSED\n')
success = 1.0
return
end
success = 0.0
printf('TEST FAILED\n')
endfunction
//------------------------------------
// Check whether two values are relatively
// close.
//------------------------------------
function r = close(x,y)
xx = max(abs(x),abs(y))
if xx ~= 0.0 then
r = (abs(x-y)/xx)<%eps*2
else
r = abs(x-y)<%eps*2
end
return
endfunction
Evolve your quadroots program to a stage in which it successfully
passes all of the test_quadroots tests.
|
|
|
// Set up a system
A = [ 1 2 -1; -2 -6 4 ; -1 -3 3 ] // of equations.
b = [ 1; -2; 1 ]
x = A\b // Find x with A x = b.
SCILAB should give the solution
|
A*x // Check that Ax and b are the same.
b
b - A*x ; // So that we do not miss small differences,
// it is better to check that b - A x = 0 .
The vector b−Ax is known as the residual.
| 1.0e-004 * | ||||
| 1.2300e-05 | ||||
| 0.1234 | or | 4.4400e-06 | ||
| 0.0444 |
A = [1 2 3 4 ; 1 4 9 16 ; 1 8 27 64 ; 1 16 81 256 ] A' det(A) spec(A) [D, X] = bdiag(A) inv(A)Even in the simple 4×4 case, it is tedious to evaluate determinants and inverses. But in Scilab they can be quickly calculated and used. Indices can be used to show parts of a larger matrix. For example try
A(2,3), A(1:2,2:4), A(:,2), A(3,:), A(2:$,$)In general A( i:j, k:l ) means the square sub-block of A between rows i to j and columns k to l . The ranges can be replaced by just `:' if all rows or columns are to be included. The last row or column is designated $.
c = ones(4,3)
d = zeros(20,1)
I = eye(5,5)
D = diag( [2 1 0 -1 -2] )
L = diag( [1 2 3 4], -1 )
U = rand(5,5) // U is a 5 x 5 matrix
// of uniformly distributed random numbers.
R = rand(5,5,'normal'); // R is a 5 x 5 matrix
// of normally distributed random numbers.
(More information on each of these commands can be found
with help.)
Matrix-matrix and matrix-vector multiplication work as expected,
provided the dimensions agree. Matrices and vectors can both be
multiplied by scalars. In the next example, B is another
4×4 matrix and c is a column vector of length 4
(i.e. a 4×1 matrix).
B = [1 1 0 0
0 2 1 0
0 0 3 1
0 0 0 4 ] // Rows can be separated by
// making a new line as well as using `;`.
c = [1; 0; 0; -1]
5*B // Multiply scalars and matrices.
B*c // Multiply matrices and vectors.
A*B // Matrix by matrix multiplication.
B*A // Note: AB is not the same as BA !
Some of the following commands do not work. There is no harm in
trying them.
c*c // Wrong dimensions for matrix multiplication. c*A // Wrong dimensions for matrix multiplication. c' // The transpose of c, i.e. a row vector. c'*A // Now matrix multiplication is permitted. c*c' // This multiplies a 4 x 1 by a 1 x 4 matrix. c'*c // What is this quantity usually called?
x = A^(-1)*b // Solve the equation A x = b A*x , b
// Check x is correct by making
// sure it satisfies the equations.
resid = b - A*x // Check the residual is zero,
// at least to within roundoff.
In fact it is far quicker, and usually more accurate, to solve
equations using the backslash operator ( \ )
introduced in the first section; rather than calculating with the
inverse A−1. The next lines use the backslash command to the
solve the equations Ax=b, and check that it gives the same
answers as those that were calculated using A−1.
x1 = A \ b // This is the fastest way to solve A x = b x - x1 // Here both answers are the same (up to roundoff error).
|
function z=f(x,y) z=1.0/(x+y-1) endfunctionCreate a right hand side vector b which is the first column of A, b = A(:,1). We are now going to solve the equations Ax=b. This is a common test problem in linear algebra. (What is the true solution x to the equations Ax=b?)
| xk | .5 | .7 | .9 | 1.3 | 1.7 | 1.8 |
| yk | .1 | .2 | .75 | 1.5 | 2.1 | 2.4 |
x = [.5 .7 .9 1.3 1.7 1.8 ]' y = [.1 .2 .75 1.5 2.1 2.4 ]'(Vectors are discussed in detail in the next lesson; but we can use them to draw graphs without knowing all the details.) To graph y against x use the plot command.
plot2d(x,y, style=-1)The graph should now appear. (If not, it may be hidden behind other windows. Click on the icon `Figure No. 1' on the Windows task bar to bring the graph to the front.) This graph marks the points with an `+'. Other types of points can be used by changing the style=-1 in the plot2d command. (Use help plot2d to find out the details.)

xbasc()Commands starting with x generally are associated with graphics commands (This comes from the X window system used on Unix machines). So xbasc(), is a basic graphix command which clears the graphics window. From the graph it is clear that the data is approximately linear, whereas this is not so obvious just from the numbers. Good graphs quickly show what is going on! When you have finished looking at the graph, just click on any visible part of the command window. More commands can then be typed in.
plot2d( x, y ) // The simplest plot commandThis simple command does not present the data here very well. It is hard to see how many points were in the original data. It is really better to plot just the points, as the lines between points have no significance; they just help us follow the set of measurements if there are several data sets on the one graph. If we insist on joining the points it is important to mark the individual points as well.

xbasc() x = [.5 .7 .9 1.3 1.7 1.8 ]'; y = [.1 .2 .75 1.5 2.1 2.4 ]'; plot2d(x,y,style=-1) x_vals = [ .5 2]'; // The X-coords of the endpoints. y_vals = [ 0 2.7 ]'; // The Y-coordinates plot2d(x_vals,y_vals) legends(['Line of Best Fit';'Data'],[1,-1],5) xtitle( 'Data Analysis')Note that x_vals contains the x-coordinates, y_vals contains the y-coordinates, and the two points are joined by a line because we don't specify a style (default style is a line).
| n | 3 | 5 | 9 | 17 | 33 | 65 |
| sn | .257 | .0646 | .0151 | 3.96×10−3 | 9.78×10−4 | 2.45×10−4 |
n = [ 3 5 9 17 33 65 ]';
s = [ 2.57e-1 6.46e-2 1.51e-2 ...
3.96e-3 9.78e-4 2.45e-4 ]' ;
xbasc() // clear the previous plot
plot2d( n, s, style=-1 ) // This is a poor plot!!
In fact it is hard to read the values of sn back from the
graph. However a plot of log(n) against log(sn) is much clearer.
To produce a plot on a log scale we can use either of the commands
xbasc(); plot2d( log10(n), log10(s), style=-1) xbasc(); plot2d( n, s, style=-1 , logflag = 'll')

|
|
function [y]=f(x) y = x*abs(x)/(1+x^2); endfunctionThis will define a Scilab function with the name f. We can then evaluate the function as such
f(3)To produce a plot of this function we can use the fplot2d command. For instance to plot the function f in the interval [0,1] we use
x = (-5:0.1:5)'; fplot2d(x,f)The first command produces a column vector of x values from 0 to 1 in steps of .1. The second produces the plot of the function f.
x = (-5:.1:5)' ; y = x .* abs(x) ./ ( 1 + x.^2) ; plot2d( x , y )The first command produces a vector of x values from -5 to 5 in steps of .1. That is the column vector x = [−5,−4.9,…,0,…4.9,5]′. The vector y contains the values of f at these x values. As there are so many points the graph of x against y looks like a smooth curve. The novelties here are the operators .* , ./ and .^ in the second command. These are the so called component-wise operators. In the above example x is a column vector of length 101 and abs(x) is the column vector whose ith entry is |xi|. The formula x*abs(x) would be wrong, as this tries to multiply the 1×101 matrix x by the 1×101 matrix abs(x). However the component-wise operation x.*abs(x) forms another vector of length 101 with entries xi|xi|. Continuing to evaluate the expression using component-wise arithmetic, we get the vector y of length 101 whose entries are xi|xi|/(1+xi2). (Of course we should not become overconfident. This rule for dividing by vectors cannot be used in Mathematics proper.) Note that p.*q and p*q are entirely different. Even if p and q are matrices of the same size and both products can be legitimately formed, the results will be different. Before more exercises, we show how to add further curves to the graph above. Suppose we want to compare the function we have already drawn, with the functions x|x|/(5+x2) and x|x|/([1/5]+x2). This is done by the following three additional commands. (Remember x already contains the x-values used in the plot. These new commands are most easily entered by editing previous lines.)
y2 = x .* abs(x) ./ (5 + x.^2) ; y3 = x .* abs(x) ./ ( 1/5 + x.^2) ; plot2d(x,[y y2 y3])Note that x needs to be a column vector for this to work.
|
\\ Be sure to put quotes
xtitle('Exercise 3.4 Kim Lee.') \\ around the title.
Bring the plot window to the front. The title should appear on the
graph. If everything is okay, click on the `file' menu →
`print scilab' item in the graph window. If everything is set up correctly, the
graph will appear on the printer attached to your computer.
After printing a graph, it is useful to take a pen and label the axes (if
that was not done in SCILAB), and perhaps explain the various points or
curves in the space underneath. Alternatively before printing the graph, use
the SCILAB command xtitle with two optional arguments and the legend
argument of the plot2d command for a more professional appearance.
For instance
xbasc()
plot2d(x,[y y2 y3],leg='function y@function y2@function y3')
xtitle('Plot of three functions','x label','y label')
Another option is to use the graphics window file menu item, export, or
the xbasimp command to print the
current graph to a file so that it can be printed later. For example to
produce a postscript file use the command
xbasimp(0,'mygraph.eps')This will write the current graph (associated with graphics window 0) to the file mygraph.eps. This file can be stored and printed out later.
|
v = [-4,-3,1] p = poly(v,'x','coeff')We can also build up a polynomial in a more natural way.
x = poly(0,'x') // Seed a Polynomial using variable 'x' p = x^2 - 3*x - 4 // Represents x^2 - 3 x -4There are many useful functions that can be applied to polynomials. We can find the roots (numerically) of a polynomial, evaluate a polynomial or find its derivative.
z = roots( p ) z(1)^2 -3*z(1) -4 // Two ways to evaluate the poly. horner(p,z(1)) // at the first root. derivat(p) // Calculate the derivative of the polynomialThe Scilab polynomial methods generalise to rational functions. For instance
x = poly(0,'x') // Seed a Polynomial using variable 'x'
p = x^2 - 3*x - 4 // Represents x^2 - 3 x -4
r = x/p // Represents the rational
// function x/(x^2 - 3 x -4)
horner(r,1.0) // Evaluate r at x=1
|
function y=runge(t) // // Runge's Function // Standard example of polynomial interpolation // creating oscillations // y=(1.0)./(1+25*t.^2) endfunctionNote we have put parentheses around the (1.0) to ensure that the ./ command is used. An expression of the form 1./(..) would actually use the / matrix operator! Consider a polynomial
|
tt = -1:0.01:1; yy = horner(pp,tt)Hint: You can use the standard backslash operator to solve for the coefficients once you have the Vandermonde matrix. The feval command together with an appropriately defined function
function z=vandermonde(t,q) z=t.^q; endfunctionmay be of help in producing the associated Vandermonde matrix. You might like to use deff to define your function
deff('z=vandermonde(t,q)','z=t.^q')
This is useful for short functions.
|
tdata = cos((1:2:(2*5-1))*%pi/(2*5)) ydata = runge(tdata) chebp = polyfit(tdata,ydata) tt = -1:.01:1; rr = runge(tt); yy = horner(chebp,tt); xbasc(); plot2d(tt,yy,style=1); plot2d(tt,rr,style=2); plot2d(tdata,ydata,style=-1)The results are more accurate than with equally spaced points or Taylor polynomials but still not very good: try to explain these observations.
tdata = -1:.5:1; ydata = runge(tdata); ddata = splin(tdata,ydata); tt = -1:.01:1; rr = runge(tt); ss = interp(tt,tdata,ydata,ddata);Thus the spline approximation can then be graphed using
xbasc(); plot2d(tt,ss,style=1); plot2d(tt,rr,style=2); plot2d(tdata,ydata,style=-1);
tdata = 0:1:10; ydata = [0 0 0 0 0.1 1.8 1.9 2.0 2.0 2.0 2.1];
![]() |
ddata = splin(tdata,ydata, 'monotone');Now you can use tdata and ydata values together with the new derivative values to produce an interpolant using the interp function.
A = diag(ones(20,1),-1) -2*diag(ones(21,1)) + diag(ones(20,1),1);This should produce a 21×21 tridiagonal full matrix. You can convert to sparse storage using the sparse command.
A = sparse(A);With sparse matrices it is useful to look at the non-zero structure of the matrix. The book "Engineering and Scientific Computing with Scilab" edited by Claude Gomez provides some very helpful hints on working with SCILAB. In particular they have produced some code which mimic the spy command form MATLAB. Here I have reproduced the code, encapsulated in a function.
//------------------------------------------------------- function spy(A) // Mimic the Matlab spy command // Draw the non zero components of a matrix // Taken from Engineering and Scientific Computing with Scilab'' // edited by Claude Gomez [i,j] = find(A~=0) [N,M] = size(A) xsetech([0,0,1,1],[1,0,M+1,N]) xrects([j;N-i+1;ones(i);ones(i)],ones(i)); xrect(1,N,M,N); endfunction //-------------------------------------------------------Copy this code into your SCILAB workspace and execute it on the matrix A.
xbasc();spy(A)The xbasc() is there just to clear the graphics window if you have already used it.
n=21; B = diag(sparse(ones(n-1,1)),-1) ...
- 2*diag(sparse(ones(n,1))) + diag(sparse(ones(n-1,1)),1);
Check out the matrix by printing out its entries or even printing
out a full representation of B.
B full(B)Don't do this with large matrices! Also use spy to check out the structure. The sparse storage format is essentially a list of ij entries and corresponding matrix values v of the non-zero entries in the matrix. For instance try out
//------------------------------------------------------- dij = [(1:n)', (1:n)']; // i j coordinates of diagonal dv = [-2*ones(n,1)]; // value of -2 down diagonal udij = [ (1:n-1)',(2:n)']; // i j coordinates of upper diagonal udv = [ ones(n-1,1) ]; // value of 1 for upper and lower diagonals ldij = [ (2:n)',(1:n-1)']; // i j coordinates of lower diagonal ij = [dij ; udij ; ldij ]; // concatenate all the i, j coordinates v = [dv ; udv ; udv ]; // concatenate all the values C=sparse(ij,v) // create sparse matrix full(C) //-------------------------------------------------------Make sure you know what this code is doing. Try a smaller value of n and printout each step.
//-------------------------------------------------------
function [a1,a2,a3,g1,g2,g3]=mesh_examples()
//
// Code taken from the help file for mesh2d. Produces 3
// matrices and graphs associated with triangulating
// a circular region with a hole
//
// FIRST CASE
theta=0.025*[1:40]*2.*%pi;
x=1+cos(theta);
y=1.+sin(theta);
theta=0.05*[1:20]*2.*%pi;
x1=1.3+0.4*cos(theta);
y1=1.+0.4*sin(theta);
theta=0.1*[1:10]*2.*%pi;
x2=0.5+0.2*cos(theta);
y2=1.+0.2*sin(theta);
x=[x x1 x2];
y=[y y1 y2];
[nu,a1]=mesh2d(x,y);
nbt=size(nu,2);
jj=[nu(1,:)' nu(2,:)';nu(2,:)' nu(3,:)';nu(3,:)' nu(1,:)'];
as=sparse(jj,ones(size(jj,1),1));
ast=tril(as+abs(as'-as));
[jj,v,mn]=spget(ast);
n=size(x,2);
g=make_graph('foo',0,n,jj(:,1)',jj(:,2)');
g('node_x')=300*x;
g('node_y')=300*y;
g('default_node_diam')=10;
g1 = g;
// SECOND CASE !!! NEEDS x,y FROM FIRST CASE
x3=2.*rand(1:200);
y3=2.*rand(1:200);
wai=((x3-1).*(x3-1)+(y3-1).*(y3-1));
ii=find(wai >= .94);
x3(ii)=[];y3(ii)=[];
wai=((x3-0.5).*(x3-0.5)+(y3-1).*(y3-1));
ii=find(wai <= 0.055);
x3(ii)=[];y3(ii)=[];
wai=((x3-1.3).*(x3-1.3)+(y3-1).*(y3-1));
ii=find(wai <= 0.21);
x3(ii)=[];y3(ii)=[];
xnew=[x x3];ynew=[y y3];
fr1=[[1:40] 1];fr2=[[41:60] 41];fr2=fr2($:-1:1);
fr3=[[61:70] 61];fr3=fr3($:-1:1);
front=[fr1 fr2 fr3];
[nu,a2]=mesh2d(xnew,ynew,front);
nbt=size(nu,2);
jj=[nu(1,:)' nu(2,:)';nu(2,:)' nu(3,:)';nu(3,:)' nu(1,:)'];
as=sparse(jj,ones(size(jj,1),1));
ast=tril(as+abs(as'-as));
[jj,v,mn]=spget(ast);
n=size(xnew,2);
g=make_graph('foo',0,n,jj(:,1)',jj(:,2)');
g('node_x')=300*xnew;
g('node_y')=300*ynew;
g('default_node_diam')=10;
g2=g;
// REGULAR CASE !!! NEEDS PREVIOUS CASES FOR x,y,front
xx=0.1*[1:20];
yy=xx.*.ones(1,20);
zz= ones(1,20).*.xx;
x3=yy;y3=zz;
wai=((x3-1).*(x3-1)+(y3-1).*(y3-1));
ii=find(wai >= .94);
x3(ii)=[];y3(ii)=[];
wai=((x3-0.5).*(x3-0.5)+(y3-1).*(y3-1));
ii=find(wai <= 0.055);
x3(ii)=[];y3(ii)=[];
wai=((x3-1.3).*(x3-1.3)+(y3-1).*(y3-1));
ii=find(wai <= 0.21);
x3(ii)=[];y3(ii)=[];
xnew=[x x3];ynew=[y y3];
[nu,a3]=mesh2d(xnew,ynew,front);
nbt=size(nu,2);
jj=[nu(1,:)' nu(2,:)';nu(2,:)' nu(3,:)';nu(3,:)' nu(1,:)'];
as=sparse(jj,ones(size(jj,1),1));
ast=tril(as+abs(as'-as));
[jj,v,mn]=spget(ast);
n=size(xnew,2);
g=make_graph('foo',0,n,jj(:,1)',jj(:,2)');
g.node_x=300*xnew;
g('node_y')=300*ynew;
g('default_node_diam')=3;
g3=g;
endfunction
//--------------------------------------
// Now run the function to produce the
// example matrices and associated graphs
//--------------------------------------
[A1,A2,A3,g1,g2,g3] = mesh_examples();
//-------------------------------------------------------
If you have loaded the previous snippet of code into SCILAB
there should now be 3 new matrices A1, A2, A3 defined in
your workspace. Try spy on these matrices.
I obtained the following spy plot of the matrix A3

xbasc(); show_graph(g1);Actually I think the g2 and g3 are more interesting. You can use the options submenu of the graph menu to number the nodes. This can help to understand the strange structure of the corresponding sparse matrix. I obtained the following plot of the graph associated with matrix A3

A1inv = inv(A1);Look at the A1inv with the spy function. The inverse is nearly full. This is typical, the inverse of a random sparse matrix is invariably full. The moral, avoid calculating the inverse of sparse matrices!
[n,m]=size(A1); x = ones(n,1); b = A1*x;So you have a right hand side b together with a solution vector x. Now lets see how well each of the solution methods work. Use the help command to see how to call the different factorisation commands. Use the timer() command to time each calculation. For instance
timer(); x1 = A1\b ; t1 =timer() err1 = norm(x1-x)
[iperm,mrepi]=bandwr(triu(A1)); RA1 = A1(mrepi,mrepi); xbasc();spy(A1) xbasc();spy(RA1)Note that the reordered matrix has a much smaller bandwidth. Determine whether the solution methods work better on the reordered matrix.
n = 10; m = 15; x = 0:1/(n-1):1; y = 0:1/(m-1):1; u = x'*y;You can plot the value of u using the plot3d command. Namely
xbasc(); xselect(); plot3d(x,y,u)Rotate the plot (using the rotate button on the figure window) to get a better idea of the shape of the surface. You might want to try a shaded plot using the plot3d1 command.
xbasc(); xselect(); plot3d1(x,y,u)The default colour map does not produce a very nice plot. SCILAB 3.0 has three in built colour maps, jetcolormap, hotcolormap and graycolormap. In SCILAB version 2.7 it seems that the function jetcolormap is not available. Here is the code for jetcolormap taken from the SCILAB 3.0 distribution. If you are using SCILAB 2.7 or earlier, load in this function so that you run the code given in tutorials.
function [cmap] = jetcolormap(nb_colors)
// PURPOSE
// to get the usual classic colormap which goes from
// blue - lightblue - green - yellow - orange then red
// AUTHOR
// Bruno Pincon
//
r = [0.000 0.125 0.375 0.625 0.875 1.000;...
0.000 0.000 0.000 1.000 1.000 0.500]
g = [0.000 0.125 0.375 0.625 0.875 1.000;...
0.000 0.000 1.000 1.000 0.000 0.000]
b = [0.000 0.125 0.375 0.625 0.875 1.000;...
0.500 1.000 1.000 0.000 0.000 0.000]
d = 0.5/nb_colors
x = linspace(d,1-d, nb_colors)
cmap = [ interpln(r, x);...
interpln(g, x);...
interpln(b, x) ]';
cmap = min(1, max(0 , cmap)) // normally not necessary
endfunction
You can set the new color map using the xset command.
Try
xset("colormap",jetcolormap(64))
xbasc(); xselect();
plot3d1(x,y,u)
function u = saddle_function(x,y) // // Saddle point function to be used as // boundary and exact solution // u = (x-0.5)^2 - (y-0.5)^2 endfunctionLoad this function into SCILAB. The boundary nodes correspond to the i, j values i=1, i=n, j=1 and j=m. So lets set those nodes to have the value given by the function saddle_function.
for i=1:n
for j=1:m
// If statement to determine boundary nodes
if( i==1 | i==n | j==1 | j==m) then
u(i,j) = saddle_function(x(i),y(j));
else
u(i,j) = 1.0;
end
end
end
![]() |
|
|
|
//--------------------------------------------
// Define Functions
//--------------------------------------------
function [A,b] = create_matrix_1(n,m)
//
// Create a matrix associated with a grid
// nodes with x and y coordinates given as
// input, connected by springs with nodes
// on the boundary set the function boundary
//
dx = 1/(n-1);
dy = 1/(m-1);
dx2 = 1/dx^2;
dy2 = 1/dy^2;
x = 0:dx:1;
y = 0:dy:1;
A = speye(n*m,n*m);
b = zeros(n*m,1);
for i=1:n
for j=1:m
index = i + (j-1)*n;
if( i==1 | i==n | j==1 | j==m) then // Boundary nodes
b(index) = saddle_function(x(i),y(j));
A(index,index) = 1.0;
else // Interior Nodes
b(index) = force;
A(index,index) = -2.0*(dx2 + dy2);
A(index,index+1) = dx2;
A(index,index-1) = dx2;
A(index,index+n) = dy2;
A(index,index-n) = dy2;
end
end
end
endfunction
Load this function into SCILAB, and create the matrix A and
right hand side b corresponding to a 5 by 5 grid by using the
command
[A,b]=create_matrix_1(5,5)This will produce an error since the create_matrix function needs the variable force set. Lets set it to 0 and try the command
force = 0.0; [A,b]=create_matrix_1(5,5)How large is the matrix A. What is its' structure. Here is the code for the spy function which is useful to view the structure of the matrix.
function spy(A) // Mimic the Matlab spy command // Draw the non zero components of a matrix [i,j] = find(A~=0) [N,M] = size(A) // wrect=[x,y,w,h] frect=[xmin,ymin,xmax,ymax] xsetech([0,0,1,1],[1,0,M+1,N]) xrects([j;N-i+1;ones(i);ones(i)],ones(i)); // [x,y,w,h] xrect(1,N,M,N); endfunction
function [U] = solve_equation_1(A,b)
//
// Wrapper for linear solver
//
U = A\b
endfunction
function run_problem(n,m)
//
// Setup a problem, create matrix,
// solve and then plot result.
//
// Input:
// n,m, number of grid points in x and y direction.
//
x = 0:1/(n-1):1;
y = 0:1/(m-1):1;
// Setup creation and solver routines
create_matrix = create_matrix_1
solve_equation = solve_equation_1
printf('Create Matrix:')
timer()
[A,b] = create_matrix(n,m);
printf(' time = %g\n',timer())
printf('Solve Matrix problem:')
timer()
U = solve_equation(A,b)
printf(' time = %g\n',timer())
u = matrix(U,n,m);
// Plot the solution
xset("colormap",jetcolormap(64))
xbasc(); xselect();
plot3d1(x,y,u)
endfunction
Now we should run our code. We just have to run the
run_problem function. For instance to run the code
for a 10 by 15 grid use the command
run_problem(10,15)When running the previous fragment of code I obtained the following plot.
![]() |
function [A,b] = create_matrix_2(n,m)
//
// Create a matrix associated with a grid
// nodes with x and y coordinates given as
// input, connected by springs with nodes
// on the boundary set the function boundary
//
dx = 1/(n-1);
dy = 1/(m-1);
dx2 = 1/dx^2;
dy2 = 1/dy^2;
x = 0:dx:1;
y = 0:dy:1;
//-----------------------
// Setup Matrix
//----------------------
A = spzeros(n*m,n*m);
//
// Setup interior problem
//
d0 = -2*(dx2+dy2)*ones(n*m,1);
dmn = dy2*ones((m-1)*n,1)
dpn = dy2*ones((m-1)*n,1)
dm1 = dx2*ones(n*m-1,1)
dp1 = dx2*ones(n*m-1,1)
//
// Apply Boundary condition
//
d0(n:n:n*m) = 1
d0(1:n:n*m) = 1
d0(1:n) = 1;
d0(n*(m-1):n*m) = 1
dmn(1:n:n*(m-1)) = 0
dmn(n:n:n*(m-1)) = 0
dmn(n*(m-2):n*(m-1)) = 0
dpn(1:n:n*(m-1)) = 0
dpn(n:n:n*(m-1)) = 0
dpn(1:n) = 0
dm1(n-1:n:n*m-1) = 0
dm1(n:n:n*m-1) = 0
dm1(1:n-1) = 0
dm1(n*(m-1):n*m-1) = 0
dp1(n+1:n:n*m-1) = 0
dp1(n:n:n*m-1) = 0
dp1(1:n-1) = 0
dp1(n*(m-1):n*m-1) = 0
A = diag(sparse(d0)) + diag(sparse(dm1),-1) + diag(sparse(dp1),1) + ...
diag(sparse(dmn),-n) + diag(sparse(dpn),n)
//----------------
// Setup rhs
//----------------
b = force*ones(n*m,1);
//
// Apply boundary condition
//
b(1:n) = feval(x,y(1),saddle_function);
b(n*(m-1)+1:n*m) = feval(x,y(m),saddle_function);
b(1:n:n*(m-1)+1) = feval(x(1),y,saddle_function)';
b(n:n:n*m) = feval(x(n),y,saddle_function)';
endfunction
Convince yourself that this code is doing the same as the previous
implementation, i.e. compare the results from the two
create_matrix functions.
function [x,i] = conjugate_gradient(A,b,x0,imax,tol,iprint)
//
// Try to solve linear equation Ax = b using
// conjugate gradient method
//
// Input
// A: matrix or function which applies a matrix, assumed symmetric
// b: right hand side
// x0: inital guess
// imax: max number of iterations
// tol: tolerance used for residual
//
// Output
// x: approximate solution
// i: number of iterations
//
//------------------------------
// Check and set input arguments
//------------------------------
nargin = argn(2)
if nargin<2
error('need at least 2 input arguments')
end
if nargin<3
x0 = zeros(b);
end
if nargin<4
imax = 2000;
end
if nargin<5
tol = 1e-8
end
if nargin<6
iprint = 0
end
function y = apply_A(x)
if (typeof(A)=='function') then
y = A(x)
else
y = A*x
end
endfunction
//-------------------------------
i=0
x = x0
r = b - apply_A(x)
d = r
rTr = r'*r
rTr0 = rTr
while (i<imax & rTr>tol^2*rTr0),
q = apply_A(d)
alpha = rTr/(d'*q)
x = x + alpha*d
if modulo(i,50)==0 then
r = b - apply_A(x)
else
r = r - alpha*q
end
rTrOld = rTr
rTr = r'*r
bt = rTr/rTrOld
d = r + bt*d
i = i+1
if modulo(i,iprint)==0 then
printf('i = %g rTr = %20.15e \n',i,rTr )
end
end
if i==imax then
warning('max number of iterations attained')
end
endfunction
Load this function into SCILAB and try on a small matrix
and rhs such as
n=21; B = diag(sparse(ones(n-1,1)),-1) - 2*diag(sparse(ones(n,1))) + ... diag(sparse(ones(n-1,1)),1); c = ones(n,1);by using the command
x = conjugate_gradient(B,c)Well that is nice, but we can see a little more detail if we use a few more arguments,
x = conjugate_gradient(B,c,zeros(c),50,1e-9,1)The 3rd argument provides an initial guess for the iterative scheme, the 4th gives provides a limit on the number of iterations, the 5th a tolerance on the terminating relative residual, and the 6th argument give the regularity of diagnostic output. The results show that the square of the residual rTr decreases (slowly) until the 11th iteration which drops to zero. This is typical of the method. So finally we can use the conjugate gradient method to try to solve our membrane problem. Our run_problem function sets up the solve_equation function. To use the conjugate gradient method set up the run_problem function to use solve_equation_2 function defined below.
function [U] = solve_equation_2(A,b) // // Get good initial guess, well at least one that // satisfies BC // x0 = b U = conjugate_gradient(A,b,x0,length(x0),1e-9,0) endfunction
function [A,b] = create_matrix_3(n,m)
//
// Create a matrix operator associated with a grid
// nodes with x and y coordinates given as
// input, connected by springs with nodes
// on the boundary set the function boundary
//
dx = 1/(n-1);
dy = 1/(m-1);
x = 0:dx:1;
y = 0:dy:1;
//-----------------------
// Setup Matrix
// A function is being created
// which will be returned
// instead of a matrix
//----------------------
function y = A(x)
//
// Setup finite difference operator for Laplacian
//
dx = 1/(n-1);
dy = 1/(m-1);
dx2 = 1/dx^2;
dy2 = 1/dy^2;
x = matrix(x,n,m)
y = x
J = 2:m-1
I = 2:n-1
y(I,J) = -2.0*(dx2+dy2)*x(I,J) + dx2*x(I+1,J) ...
+ dx2*x(I-1,J) + dy2*x(I,J+1) + dy2*x(I,J-1)
y = y(:)
endfunction
//----------------
// Setup rhs
//----------------
b = force*ones(n*m,1);
//
// Apply boundary condition
//
b(1:n) = feval(x,y(1),saddle_function);
b(n*(m-1)+1:n*m) = feval(x,y(m),saddle_function);
b(1:n:n*(m-1)+1) = feval(x(1),y,saddle_function)';
b(n:n:n*m) = feval(x(n),y,saddle_function)';
endfunction
In this case the procedure returns not a sparse matrix
A but a procedure for apply A to a vector.
J = 2:m-1
I = 2:n-1
y(I,J) = -2.0*(dx2+dy2)*x(I,J) + dx2*x(I+1,J) ...
+ dx2*x(I-1,J) + dy2*x(I,J+1) + dy2*x(I,J-1)
function A = elastic(n) A = diag(ones(n-1,1),-1) -2*diag(ones(n,1)) + diag(ones(n-1,1),1); endfunctionUse this function to produce a 5 by 5 matrix A. Then use the SCILAB command lu to produce the L and U factors of A. Here is my code
[L,U] = lu(A)and here is the matrix I obtained
A = ! - 2. 1. 0. 0. 0. ! ! 1. - 2. 1. 0. 0. ! ! 0. 1. - 2. 1. 0. ! ! 0. 0. 1. - 2. 1. ! ! 0. 0. 0. 1. - 2. !and the L and U factors
U = ! - 2. 1. 0. 0. 0. ! ! 0. - 1.5 1. 0. 0. ! ! 0. 0. - 1.3333333 1. 0. ! ! 0. 0. 0. - 1.25 1. ! ! 0. 0. 0. 0. - 1.2 ! L = ! 1. 0. 0. 0. 0. ! ! - 0.5 1. 0. 0. 0. ! ! 0. - 0.6666667 1. 0. 0. ! ! 0. 0. - 0.75 1. 0. ! ! 0. 0. 0. - 0.8 1. !Notice that both of these matrices maintain the same banded structure as the original matrix A. Notice that the inverse of A loses the banded structure. Here is the inverse
->inv(A) ans = ! - 0.8333333 - 0.6666667 - 0.5 - 0.3333333 - 0.1666667 ! ! - 0.6666667 - 1.3333333 - 1. - 0.6666667 - 0.3333333 ! ! - 0.5 - 1. - 1.5 - 1. - 0.5 ! ! - 0.3333333 - 0.6666667 - 1. - 1.3333333 - 0.6666667 ! ! - 0.1666667 - 0.3333333 - 0.5 - 0.6666667 - 0.8333333 !This is typical, and so you should normally avoid calculating inverses in preference to LU factorisations.
z = L\b x = U\zor in one line
x = U\(L\b)Actually we should use specially designed forward and backward substitution operators to undertake the L and U solves. Unfortunately SCILAB does not provide such operators, so for this investigation we will just use the backslash operators. Of course, normally we would use the backslash operator just once
x = A\b
[Q,R]=qr(A)Here are the factors I obtained.
R = ! 2.236068 - 1.7888544 0.4472136 0. 0. ! ! 0. 1.6733201 - 1.9123658 0.5976143 0. ! ! 0. 0. 1.4638501 - 1.9518001 0.6831301 ! ! 0. 0. 0. 1.3540064 - 1.9694639 ! ! 0. 0. 0. 0. - 0.8090398 ! Q = ! - 0.8944272 - 0.3585686 - 0.1951800 - 0.1230915 0.1348400 ! ! 0.4472136 - 0.7171372 - 0.3903600 - 0.2461830 0.2696799 ! ! 0. 0.5976143 - 0.5855400 - 0.3692745 0.4045199 ! ! 0. 0. 0.6831301 - 0.4923660 0.5393599 ! ! 0. 0. 0. 0.7385489 0.6741999 !The R matrix is upper triangular, but the Q matrix is near full. You might think it is somewhat hard to work with. But calculate QT Q. It is essentially the identity. This is one of the properties of orthogonal matrices. Also calculate the 2-norm of A and R. They should be the same. This is another property of orthogonal matrices. Application of orthogonal matrices leaves the norm invariant. Let's solve A x = b, with b = [1 1 ... 1]T using the QR factorisation. As Q is orthogonal we can solve Q z = b by z = QT b. Hence we can solve the equation with the code
z = R\(Q'*b)
function A = vandermonde(n,m)
t = (0:1/(n-1):1)';
A = zeros(n,m);
for j=1:m
A(:,j) = t.^(j-1);
end
endfunction
Here is the famous Hilbert matrix
function H = hilbert(n)
deff('z = hh(i,j)','z = 1/(i+j-1)')
H = feval(1:n,1:n,hh)
endfunction
Here is the elastic example used before.
function A = elastic(n) A = diag(ones(n-1,1),-1) -2*diag(ones(n,1)) + diag(ones(n-1,1),1); endfunctionHere is some code to investigate the condition number of the square vandermonde matrix over a range of sizes.
function [] = test_vandermonde(m)
data = zeros(1,m-1);
for i=1:m-1
A = vandermonde(i+1,i+1);
data(i) = cond(A);
end
scf(0)
xbasc()
xtitle('condition VanderMonde')
plot2d(2:m,data,style=-1)
endfunction
Here is the plot produce by the test_vandermonde function.
|
|
function udot=f(t,u) udot = k * u * ( 1 - u ); endfunction(Although t is not used in calculating udot, the SCILAB function ode requires the function f to be passed to ode must have two input arguments (in the correct order). Use help ode for more information.) Having set up the problem, the differential equation is solved in just one SCILAB line using ode!
t=0:100; k = 0.2; u = ode( 1/10000, 0.0, t, f ); xbasc(); plot2d( t,u)Note that the value of k is available to ode and to f. The function ode also has parameters which can be adjusted to control the accuracy of the computation. Mostly, these are not necessary for just plotting the solution to ordinary problems. It is always a good idea to check our the original solution by resolving the problem with higher accuracy requirements. For example, we can use ode so that the estimated absolute and relative errors are kept less than 1×10−9. This is done with the commands
uu = ode( 1/10000, 0.0, t, rtol=1e-9, atol=1e-9, f ); plot2d(t,uu,style=-1)We immediately see that the solution computed in the first call to ode lies on top of the more accurate solution curve. That is the two solutions coincide to graphical accuracy. For this simple model it is possible to find an analytic formula for u(t). In fact
|
t = 0:100 ; c = 10000-1 ; k = .2 ; function u=exactu(t) u = 1 ./ ( 1 + c*exp(-k*t)); endfunction xbasc(); plot2d(t,u); fplot2d(t,exactu,style=-1 )The exact formula gives a curve that is the same as the numerical solution. In fact the difference between the two solutions is at most 2.5×10−7. So we can be confident in using ode to solve the problems below.
|
This simple model problem also has a formula for the exact solution. However in SCILAB it is very easy to get a numerical solution; and this can be done even in complicated problems when there is no simple formulae for the solution. Using SCILAB we can simply explore the model by altering the parameters and plotting the solutions!
|
|
|
|
function dx = chemical(t,x) f1 = k1*x(1)*x(2) f2 = k2*x(1)*x(1)*x(3) dx(1) = -f1 - 2*f2 dx(2) = -f1 + f2 dx(3) = f1 - f2 dx(4) = f1 endfunction(Be careful to get the t and x arguments in the correct order!) Setting up the rate constants and an initial condition, we can obtain approximations to the solution at the times t = 0, 0.001, 0.002, ... 0.1 using the code
k1 = 1e2 k2 = 1 t = 0:0.001:0.1; x0 = [1 ;1; 0; 0]; x = ode(x0,0,t,chemical);It is nice to produce some graphical output. I used
xbasc()
plot2d(t,x(1,:),style=1)
plot2d(t,x(2,:),style=2)
plot2d(t,x(3,:),style=3)
plot2d(t,x(4,:),style=4)
legends(['A','B','C','D'],[1 2 3 4],"ur")
xtitle('Chemical ODE')
to produce the plot
|
xbasc();plot2d(t',y',leg='A@B@C')I obtained the plot
ymax = max(y,'c');The trick is now to scale each row of y by the corresponding entries of ymax. I came up with
yscaled = diag(1.0./ymax)*y;Can you see what this is doing? Now you can plot t versus yscaled with
xbasc(); plot2d(t',yscaled') legends(['A';'B';'C'],[1;2;3])I suggest producing a legend so that you can see which curve corresponds to which variable. The format of the command associates a character string (such as 'A' with a line style 1. I now obtained the plot
xbasc();
plot2d('ln', t',yscaled')
legends(['A';'B';'C'],[1;2;3])
Here is the plot now
| (10.1) |
function z = eqn1(x,y) z = k*(x**2+y**2) - 2; endfunction function z = eqn2(x,y) z = k*sin(x*y); endfunctionWe can plot the functions using the fplot3d or the fcontour2d functions. Try
k = 1; x = -4:0.1:4; y = x; xbasc(); fplot3d(x,y,eqn2)Notice that we have set the value of k and it is being passed through to the function eqn1 implicitly. Contour plots can sometimes be easier to work with. A contour plot for the first function can be obtained via
xbasc(); fcontour2d(x,y,eqn1,[0 5 10 20 25]);Not that we have asked for the contour lines for 0 up to 25 in steps of 5. Make sure you have a look at the plot. Now let us over plot with the contours of the second function.
fcontour2d(x,y,eqn2, [0,1]);The overall plot is quite complicated (at least to me). Perhaps you might like to plot both functions separately to get an idea of the "shape" of both. But with both contour plots on the same graph, use the 2d zoom facility of SCILAB to get an approximate solution (3 sig figs) of equation (10.1) (for k=1).
function y = f(x) y(1) = eqn1(x(1),x(2)); y(2) = eqn2(x(1),x(2)); endfunctionwhere the functions eqn1 and eqn2 were defined in the last section. Of course you could define the function without using eqn1 and eqn2. Unfortunately the fsolve function expects a very particular form, which is different than the calling form for the plotting routines. We would store this in a file and use exec to load the function. We could then solve the equation using fsolve, as such
k=1; [x,y] = fsolve([0;0], f)Does the solution provided by fsolve match the one you discovered manually? It might be useful to get some feedback about what fsolve is doing. One way is to provide some output from the function. Redefine the function f as such:
function y = f(x)
y(1) = eqn1(x(1),x(2));
y(2) = eqn2(x(1),x(2));
printf('x(1)=%15e x(2)=%15e y(1)=%15e y(2)=%15e\n',x(1),x(2),y(1),y(2))
endfunction
Take note of the printf function. This is the formatted
print command (like the corresponding C procedure). The formatting
is accomplished using the codes like %15e
which will produce a decimal number using 15 characters.
|
|
function udot = shootode(t,u)
function y = fshoot(s)which takes an initial value for the slope at 0 and returns the value of y(1)−1. For instance, fshoot(1.0) should return approximately 4.1.
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------- function [] = heat(n,dt,f,alpha,beta) //-------------------------- // Initialise plotting //-------------------------- initMyplot() //-------------------------- // Setup parameters //-------------------------- //-------------------------- // Setup initial conditions //-------------------------- //-------------------------- // Setup Boundary conditions //-------------------------- //-------------------------- // Plot the initial conditions //-------------------------- //-------------------------- // The main time loop //-------------------------- //-------------------------- // Close //-------------------------- closeMyplot() endfunction //---------------------------------------------------------------Cut and paste this into your SCILAB file heat.sce. Now let us expand the code. First, let us look at setting up parameters, as this allows us to define some variables for subsequent use. I chose the following parameters
//-------------------------- // Setup parameters //-------------------------- c = 1.0; dx = 1/n; lambda = dt/dx^2*c; x = (0:dx:1)'; t = 0;Note that I am using the comment lines to help you place the code correctly. I needed c to define the constant in the heat equation, dx is obvious, lambda is a useful combination of parameters that occurs in the updating of the solution vector (by the way, the dt is being passed in so that you can play with changing it to test stability). I have created an x vector so that I can plot our result x versus u. A variable for time t is useful for the main time loop. Cut and Paste this code into your file pde.sce in the initial part of the function. The initial condition will be given by the value of a function f at the x values. It turns out that we also need a second u vector u1 which we create at this point. We simply use
//-------------------------- // Setup initial conditions //-------------------------- u0 = f(x); u1 = zeros(u0);We will need to define a function f. Lets do something simple like a sin curve. I used
//--------------------------------------------------------------- function u=fsin(x) k=1 u = sin(%pi*k*x) endfunction //---------------------------------------------------------------Put this in the file pde.sce file. The boundary conditions can be simply set to α at the left hand end of u0 and β at the right hand end.
//-------------------------- // Setup Boundary conditions //-------------------------- u0(1) = alpha; u0($) = beta;In most simulations it is useful to plot out results. This is particularly useful when coding up a method as it often points to errors in the code. So lets plot the initial condition. I suggest something like
//-------------------------- // Plot the initial conditions //-------------------------- myplot(x,u0)The xselect() will bring the graphics window to the front and the myplot function is just a function to plot x versus u vectors. Copy this code to the correct position within your file. Now we also have to define our plotting routine. I used
//--------------------------------------------------------------- function []=initMyplot() // // Setup pixmap, this is good for animations // xselect() f=gcf(); f.pixmap='on' endfunction function []=closeMyplot() // // Turn off pixmap mode // f=gcf(); f.pixmap='off' endfunction function []=myplot(x,u) xbasc(); plot2d(x,u,rect=[0,-1,1,1]); xstring(0.9,0.9,'time:'+string(t)); show_pixmap() endfunction //---------------------------------------------------------------The main part of this function is just the standard plot2d command. I have added show_pixmap() to make the plots look nicer when we have our time evolution working. Lets try our code. Save your file and in the SCILAB command window load in the functions and run the heat function.
n=20; heat(n,0.5/n^2,fsin,0,-1)The right boundary condition doesn't match up with the initial conditions. We could run our method with this incompatibility, but let's fix up the boundary conditions. With new boundary conditions try
n=20; heat(n,0.5/n^2,fsin,0,0)Nothing happens. Now we can go back the code and add in the time evolution part. We need a loop, we can use a for loop but I like using a while loop based on time. I.e. I suggest using
//--------------------------
// The main time loop
//--------------------------
while t<1
t=t+dt
//--------------------------------
// Update the solution vector
//--------------------------------
myplot(x,u0)
end
This loop is a little incorrect, in that it may overshoot the
final time by dt. Not to worry. The main thing now is how
to update the solution vector. The most obvious way is to use a
for loop to implement the finite difference update
|
//--------------------------------
// Update the solution vector
//--------------------------------
for j=2:n
u1(j) = (1-2*lambda)*u0(j) ...
+ lambda*u0(j+1) + lambda*u0(j-1);
end
u0 = u1;
It is always important to get the range of j correct. We
only need to work with the interior points. There are actually
n+1 entries in u0 and u1 and so the range
j=2:n corresponds to interior points.
An alternative method to the for loop is to use the array notation in SCILAB\
//--------------------------------
// Update the solution vector
//--------------------------------
u1(2:$-1) = (1-2*lambda)*u0(2:$-1) ...
+ lambda*u0(3:$) + lambda*u0(1:$-2);
u0 = u1;
This second method will be more efficient in SCILAB though the
for method is closer to what you would use in C or
FORTRAN.
Use the first method initially and change over to the
second if the evolution seems slow. As we will be plotting the
solution for every iteration the overall speed of the method will
be slow anyway. The array method becomes more important for higher
dimensional problems.
Lets see if our method works. Add in the time loop code, load in
the code with exec and run the heat command again.
You might like to increase the number of grid points to say 100.
To stop the evolution you can use the Control menu with the
pause, resume and abort commands.
Lets try another initial condition. Go back to your code and add
the following function to your utility file.
//--------------------------------------------------------------- function u=fline(x) u = 0.5-x; endfunction //---------------------------------------------------------------This is a solution of the Heat equation with the boundary conditions α = 0.5 and β = −0.5. The exact solution to the heat equation with this set of boundary conditions is the straight line between these two points. Let's check how our solver works.
exec heat.sce; n=100;heat(n,0.5/n^2,fline,0.5,-0.5)Why is the solution changing? Go back to your code and find the bug and fix it so that the boundary condition is set properly.
n=100; heat(n,0.52/n^2,fsin,0.0,0.0)After some time you should see instabilities arising. You can use cntr^C to interrupt the code and resume to start it up again.
|
//---------------------------------------------------------------
function [] = wave(n,dt,f,g,alpha,beta)
//--------------------------
// Initialise plotting
//--------------------------
initMyplot()
//--------------------------
// Setup parameters
//--------------------------
c = 1.0;
dx = 1/n;
lambda = dt^2/dx^2*c;
x = (0:dx:1)';
t=0;
//--------------------------
// Setup initial and
// boundary conditions
//--------------------------
u0 = f(x);
u0(1) = alpha; u0($) = beta;
u1 = zeros(u0);
if (g==1) then
u1(2:$-1) = u0(1:$-2);
elseif (g==-1) then
u1(2:$-1) = u0(3:$);
else
u1(2:$-1) = 0.5*(u0(1:$-2)+u0(3:$));
end
u1(1) = alpha; u1($) = beta;
u2 = zeros(u0);
u2(1) = alpha; u2($) = beta;
//--------------------------
// Plot the initial conditions
//--------------------------
xselect()
myplot(x,u0)
myplot(x,u1)
//--------------------------
// The main time loop
//--------------------------
while t<20
t=t+dt;
//--------------------------------
// Update the solution vector
//--------------------------------
for j=2:n
u2(j) = (2-2*lambda)*u1(j) - u0(j) ...
+ lambda*u1(j+1) + lambda*u1(j-1);
end
u0=u1;
u1=u2;
myplot(x,u2)
end
//--------------------------
// Close
//--------------------------
closeMyplot()
endfunction
//---------------------------------------------------------------
n=100; wave(n,1/n,fsin,0,0,0)Hopefully you will see an "oscillating string". Lets try a different initial condition, A bump in the middle of the string.
//--------------------------------------------------------------- function u = fbump(x) c = 0.5; w = 0.125; cl = c-w; cr = c+w; u = bool2s(x>cl & x<=cr) endfunction //---------------------------------------------------------------See how we can use comparisons to create interesting functions. Now try out this initial condition
n=100; wave(n,1/n,fbump,0,0,0)It looks cool as the disturbance reflects off the boundaries. It is amazing, we are actually getting the exact solution. You can get the bump to move either to the right or left using the g argument. Try
n=100; wave(n,1/n,fbump,1,0,0)and
n=100; wave(n,1/n,fbump,-1,0,0)
//---------------------------------------------------------------
function [] = iheat(n,dt,f,alpha,beta)
//--------------------------
// Initialise plotting
//--------------------------
initMyplot()
//--------------------------
// Setup parameters
//--------------------------
c = 1.0;
dx = 1/n;
lambda = dt/dx^2*c;
x = (0:dx:1)';
t=0;
//--------------------------
// Call f to setup initial
// conditions.
//--------------------------
u0 = f(x);
//--------------------------
// Setup the matrix A as a
// sparse matrix
//--------------------------
A = (1+2*lambda)*diag(sparse(ones(n-1,1))) ...
-lambda*diag(sparse(ones(n-2,1)),-1) ...
-lambda*diag(sparse(ones(n-2,1)),1);
//--------------------------
// Setup Boundary conditions
//--------------------------
u0(1) = alpha
u0($) = beta
bc = zeros(n-1,1)
bc(1) = lambda
bc($) = lambda
//--------------------------
// Plot the initial conditions
//--------------------------
xselect()
myplot(x,u0)
//--------------------------
// The main time loop
//--------------------------
while t<20
t = t+dt
bc = u0(2:$-1);
bc(1) = bc(1) + u0(1)*lambda
bc($) = bc($) + u0($)*lambda
u0(2:$-1) = A\bc
myplot(x,u0)
end
//--------------------------
// Close
//--------------------------
closeMyplot()
endfunction
//---------------------------------------------------------------
Try
n=100; iheat(n,1/n,fsin,0,0)Try
n=100; iheat(n,1/n,fbump,0,0)You might even like to try larger values of n to slow down the evolution!
|
Page last updated: May 8, 2006
Please direct all enquiries to: MSI webmaster Page authorised by: Dean, MSI |
| The Australian National University - CRICOS Provider Number 00120C |