0% found this document useful (0 votes)
224 views34 pages

FEEG1001 Applied Computing

This document describes a course on applied computing for aerospace engineering. It covers topics like computational definition of curves, computational analysis of geometry, and optimization. The first section discusses parametric definitions of conics and Bézier curves using projective geometry. It presents the mathematical foundations for representing conics as intersections of projectivities. Subsequent sections will cover Bézier splines, computational fluid dynamics software like XFOIL, and shape optimization techniques like BFGS and L-BFGS-B. Python code examples are provided for generating Bézier curves and interfacing with XFOIL.

Uploaded by

julinc1234
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
224 views34 pages

FEEG1001 Applied Computing

This document describes a course on applied computing for aerospace engineering. It covers topics like computational definition of curves, computational analysis of geometry, and optimization. The first section discusses parametric definitions of conics and Bézier curves using projective geometry. It presents the mathematical foundations for representing conics as intersections of projectivities. Subsequent sections will cover Bézier splines, computational fluid dynamics software like XFOIL, and shape optimization techniques like BFGS and L-BFGS-B. Python code examples are provided for generating Bézier curves and interfacing with XFOIL.

Uploaded by

julinc1234
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 34

FEEG1001: Applied Computing for Aero and

Mechanical Engineering
Alexander I J Forrester

Contents
1 Computational Definition of Curves
1.1 Lecture 1: Conics and Bezier curves . . . . . . . . .
1.1.1 Parametric Bernstein Conic . . . . . . . . . .
1.2 Lecture 2: rational conics, Bezier curves and splines
1.2.1 Properties of Bezier curves . . . . . . . . . .
1.2.2 Splines . . . . . . . . . . . . . . . . . . . . . .
1.3 Lab 1: create a Bezier spline using Python . . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

2
2
2
8
9
10
10

2 Computational analysis of geometry


13
2.1 Lecture 3: XFOIL (an example of computational analysis) . . . . 13
2.1.1 Creating the .dat and .in files using Python . . . . . . . 15
2.1.2 Running XFOIL from Python . . . . . . . . . . . . . . . . 16
2.2 Lab 2: run an XFOIL analysis of a Bezier spline aerofoil using
Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3 Optimization
3.1 Lecture 4: shape optimization . . . . . . . . . . . . . . .
3.1.1 BFGS . . . . . . . . . . . . . . . . . . . . . . . .
3.1.2 L-BFGS-B . . . . . . . . . . . . . . . . . . . . .
3.1.3 Further considerations . . . . . . . . . . . . . . .
3.2 Lab 3: conduct an aerofoil shape parameter study with
and XFOIL . . . . . . . . . . . . . . . . . . . . . . . . .
3.3 Lab4: optimise an aerofoil using Python and XFOIL . .

. . . . .
. . . . .
. . . . .
. . . . .
Python
. . . . .
. . . . .

4 Further computational geometry


4.1 Lecture 5: B-splines and NURBS
4.1.1 interpolating B-spline . .
4.1.2 Knots . . . . . . . . . . .
4.1.3 NURBS . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

19
19
20
21
22
23
25
26
27
29
29
30

a (0 )

a (1 )
a (2 )

Figure 1: a conic as the intersections of projectivities between two lines.

1
1.1

Computational Definition of Curves


Lecture 1: Conics and B
ezier curves

The classical, and initially intuitive, definition of conics is that of a plane intersecting a cone, with the angle of intersection determining the shape. Here we
hope to give more insight and intuition, particularly when considering parametric conics and then on to NURBS, by using the projective geometry definition.
We will only cover the essentials of projective geometry
Figure 1 shows a conic as the intersection of lines. The conic is defined by two
tangent lines, here through a(0) , a(1) and a(2) , a(1) (but could easily be any two of
the lines shown), and a third shoulder tangent (shown in bold). Described in this
way, the conic is a mapping or projectivity between the two tangent lines, with
this projectivity defining the shoulder tangent. The following sections cover this
projective geometry construction of conics, before moving on to Bezier cures,
splines, B-splines and NURBS. We will skip some of the intricacies of projective
geometry and the reader wanting more on in this area may wish to refer to
[Farin, 1999].
Studies on conics date back to Menaechmus (380-320 BC) [Thomas, 1939].
Conics are found in astronomy, palaeontology, aerodynamic design (see figure
2) where P-51 designer Edgar Schmued called second degree conics the shape
the air likes to touch and, indeed, many areas of everyday life (see figure 3).
1.1.1

Parametric Bernstein Conic

To define a projectivity, and so a conic, we require three pre-image and image


point pairs. Figure 4 is a reproduction of figure 1, showing these three pairs:
a(0) ! a(1) , a(1) ! a(2) , and s(1) ! s(2) . Point a(1) is the intersection of A and
2

Figure 2: a conic-based jet nozzle design for an unmanned air vehicle (provided
by Andr
as S
obester).

Figure 3: a BMX ramp with a conic profile marked-out by hand using the same
construction by projectivities as in figure 1.

a (0 )
t (1 )
s (1 )
t

(3 )

s (3 )

a (1 )
s (2 )

a (2 )

t (2 )

Figure 4: Construction of a Bernstein conic.


B, and S = s(1) s(2) is the shoulder tangent. We will position this shoulder
tangent at
s(1) =

1 (0) 1 (1)
a + a
2
2

(1)

s(2) =

1 (1) 1 (2)
a + a .
2
2

(2)

and

A point on the conic, which we will call C, in figure 4 shall be found as the
intersection of a tangent, T(t), with the conic (t is a parameter that we vary so
that t(3) (the intersection of T with the conic) traces out, i.e. moves along the
along the conic). The intersection of T with A is
t(1) = (1

t)a(0) + ta(1) ,

(3)

t(2) = (1

t)a(1) + ta(2) .

(4)

and with B is
This intuitive result is derived in Farin [1999] using the four tangent theorem
(which is also employed below). Thus as t varies from 0 to 1, the intersection
t(3) traces out the conic from a(0) to a(2) (the whole conic is traced out by
1 < t < 1).
Looking at figure 4, by bringing t(1) to a(1) , i.e. setting t = 0, we see that
the intersection of S and T,
s(3) =

1 (1) 1 (2)
t + t .
2
2
4

(5)

We now have enough information to find any point on the conic t(3) (t), but
will need to invoke the four tangent theorem. Four tangents to a conic (e.g.
A, B, S and T in figure 4) each have an intersection with the conic and three
intersections with the other three tangents. The four tangent theorem states
that the cross ratio of these four intersections equals the same constant for all
four tangents.
In general, given four points on a line, such that
a(3) = 1 a(1) +

1a

(2)

a(4) = 2 a(1) +

(2)
,
2a

and

the cross ratio of these four points is here defined as


cr(a(1) , a(3) , a(4) , a(2) ) =

(6)

Thus, from 6, 1 and 3, the cross ratio for the intersections of A


cr(a(0) , t(1) , s(1) , a(1) ) =

1/2 t
t
=
1/2 1 t
1 t

(7)

and from the four tangent theorem


cr(a(0) , t(1) , s(1) , a(1) ) = cr(t(1) , t(3) , s(3) , t(2) ).

(8)

Using 6 and 8,
cr(t(1) , t(3) , s(3) , t(2) ) =

1/2 t
.
1/2 1 t

(9)

And so:
t(3) (t) = (1

t)t(1) (t) + tt(2) (t).

(10)

Substituting 3 and 4 into 10 , we find that the point on the conic, C(t) is a
quadratic with coefficients based on our original points in figure 4:
C(t) = t(3) (t) = (1

t)2 a(0) + 2t(1

t)a(1) + t2 a(2) .

With the quadratic Bernstein basis polynomials defined as

2
bi,2 (t) =
ti (1 t)2 i , i = 0, 1, 2,
i
where the binomial coefficient

2
i

2!
,
i!(2 i)!

(11)

(12)

0.9

0.8

b 0 ,2( t)

0.7

b 2 ,2( t)

b i , 2( t)

0.6

b 1 ,2( t)
0.5

0.4

0.3

0.2

0.1

0
0

0.1

0.2

0.3

0.4

0.5

0.6

0.7

0.8

0.9

Figure 5: The Bernstein polynomials (equation 12), displaying the intuitive


property of Bezier curve control point weighting falling away from control points.
equation 11 can be re-written as a projective quadratic Bezier curve:
B(t) = C(t) = t(3) (t) =

2
X

a(i) bi,2 (t).

(13)

i=0

We see that the Bezier curve is a weighted sum of the control points, a(i) . The
influence of these control points, which is determined by their weighting bi,2 (t),
should be greatest when the curve passes closest to them and this influence
should diminish as the curve moves away. For example, bi,2 (t) should reach
a maximum when t = 0.5 and bi,2 (t) ! 0 as t ! 1. Figure 5 shows the
Bernstein polyniomial weightings (equation 12) used by the Bezier curve.
It is possible to add further control points to the sum in equation 13, weighted
using higher degree Bernstein polynomials:

n!
n
n
i
n i
bi,n (t) =
t (1 t) , where
=
,
(14)
i
i
i!(n i)!
which are shown in figure 6. Figure 7 demonstrates this by inserting a control
point in to the definition of the quadratic Bezier curve in figure 4 to produce a
cubic Bezier curve:

B(t)

3
X

a(i) bi,3 (t)

i=0

3
X
i=0

(1

a(i)

3
i

ti (1

t)3 a(0) + 3t(1

t)3

t)2 a(1) + 3t2 (1


6

t)a(2) + t3 a(3) .

(15)

0.9

0.8

b 0 , 3( t)

0.7

b 3 , 3( t)

b i , 2( t)

0.6

b 1 ,3( t)

0.5

b 2 , 3( t)

0.4

0.3

0.2

0.1

0
0

0.1

0.2

0.3

0.4

0.5

0.6

0.7

0.8

0.9

Figure 6: The Bernstein polynomials (equation 12), displaying the intuitive


property of Bezier curve control point weighting falling away from control points.
The Python code to produce this curve is included in listing 1.
Listing 1: Sample Python code to calculate and plot the cubic Bezier curve in
figure 4
import p y l a b a s p
import numpy a s np
# define control points
a=np . a r r a y ( [ [ 1 , 1 0 ] , [ 2 , 5 ] , [ 7 . 6 8 6 3 , 3 . 9 2 1 6 ] , [ 0 , 2 ] ] )
# calculate projective cubic bezier for t [0 ,1]
t=np . l i n s p a c e ( 0 , 1 , 1 0 1 )
c u b i c B e z i e r=np . z e r o s ( [ 1 0 1 , 2 ] )
f o r i in r a n g e ( 0 , 1 0 1 ) :
cubicBezier [ i , : ] = ((1
t [ i ] ) 3 a [ 0 , : ] + \
3 t [ i ] (1
t [ i ] ) 2 a [ 1 , : ] + \
3 t [ i ] 2 ( 1
t [ i ]) a [2 , : ] + \
t [ i ] 3 a [ 3 , : ] )
# p l o t cubic Bezier
p . plot ( cubicBezier [ : , 0] , cubicBezier [ : , 1])
# plot control points
p . p l o t ( a [ : , 0 ] , a [ : , 1 ] , ko )
# p l o t control polygon
p . plot (a [ : , 0] , a [ : , 1] , k )
p . show ( )

This is a rather nave implementation, which only works with four control
points. A generic Bezier curve could be produce by using the function for the
binomial coefficient in listing 2.
Listing 2: Python code to calculate the binomial coefficient
def binom ( n , i ) :
i f 0 <= i <= n :

a (0 )

a (1 )
a (2 )

a (3 )

Figure 7: A cubic Bezier curve produced by inserting an additional control point


to the quadratic Bezier curve in figure 4 (the Python code to calculate this curve
is in listing 1).
p = 1
f o r t in x r a n g e ( min ( i , n
i )):
p = (p (n
t ) ) // ( t + 1 )
return p
else :
return 0

1.2

Lecture 2: rational conics, B


ezier curves and splines

While the use of projective geometry to define conics is elegant and intuitive,
it is unsuitable for design purposes. We can project onto the the plane z = 1
(an affine plane), simply by dividing the projective points through by the zcomponent (the figures in the previous sections are in this plane):
0 IP IP 1

ax /az
aIP
ax
a=
= IP = @ aIyP /aIzP A ,
ay
az
aIzP /aIzP
where here we use IP to denote a point in projective space. Equation 11 can
now be written as a rational form as
C(t) =

(1

t)2 z (0) a(0) + 2t(1 t)z (1) a(1) + t2 z (2) a(2)


,
(1 t)2 z (0) + 2t(1 t)z (1) + t2 z (2)
IP (0)

(16)

(and also for higher degree conics) where, e.g., az


is written simply as z (0) .
These z (i) s are known as weights and the a(i) s are the control points.
8

a (0 )

z (1 ) = 0
0.25
0.5
1
2
5

z (1 ) = 100
a (1 )

a (2 )

Figure 8: Varying weights for a rational quadratic conic.


In a similar way, equation 13 can now be cast as a rational Bezier curve: 11
can be re-written as a projective quadratic Bezier curve:
Pn
(i)
i=0 wi a bi,n (t)
B(t) = P
,
(17)
n
i=0 wi bi,n (t)

where wi are the weights. Figure 4 has all weights set at unity. Figure 8 shows
the eect of varying z (1) , with z (0) , z (2) = 1. The eect is intuitive; for z (1) = 0,
C becomes a straight line from a(0) to a(2) and as z (1) ! 1, C(t) tends to the
line a(0) , a(1) , a(2) .
For the remainder of this chapter we will consider rational curves defined by
x, y coordinates of points and weightings.
1.2.1

Properties of B
ezier curves

The above derivation of a conic and so Bezier curve has lead to an intuitive form
of curve, which can be pushed and pulled around by control points. Salomon
[2006] lists a number of properties of Bezier curves (and discusses them in more
detail than we shall do here), which help in understanding their usefulness in
geometry definition and manipulation:
the weights, bi,n (t) add up to 1 (they are barycentric),
the curve passes through the two endpoints a(0) and a(n) ,
the curve is symmetric with respect to the numbering of control points, i.e.
the same result will be obtained by setting a(0) = a(n) , a(1) = a(n 1) , ..., a(n) =
a(0) ,

the first derivative can be obtained as

a(t)
=n

n
X1

a(i) bi,n

1 (t),

where

a(i) = a(i+1)

a(i) ,

(18)

the weight functions bi,n have a maximum at t = i/n, that is the influence
of the control points is spaced evenly with respect to t,

the two end tangents, derived from 18 as a(0)


= n(a(1)
a(0) ) and
(n)
(n
1)
(1)

a(1)
= n(a
a
), are easily controlled by moving a and a(n 1)
respectively,
the Bezier curve is controlled globally by editing control point(s) and
weighting(s), that is changing one control point will aect the entire curve
(note how in figures 5 and 6 the weighting of each point has an influence
over all values of t), with the aect greatest close to the control point,
the curve is contained within the control polygon (within its convex hull ),
as seen in figure 7, where the curve lies within the triangle formed by
a(0) , a(1) , a(2) and then that formed by a(1) , a(2) , a(3) , and
the curve is invariant to affine transformations, e.g. can be shifted up/down, left/right, rotated, reflected (but is not invariant under projections).
1.2.2

Splines

A series of conics or Bezier curves can be concatenated to produce a more


complex curve. The process is quite straightforward. For two successive curves
of degree n and m, defined by control points p(i) , i = 0, 1, ..n and q(i) , i =
0, 1, ..m to meet, the end and start points must coincide, i.e. p(n) = q(0) . For
a smooth connection, p (n) = q (0) . Thus from (18) and the end tangent note
above,
m
n
p(n) = q(0) =
q(1) +
p(n 1) .
(19)
m+n
m+n
Note that we do not need to specify the 1st and nth points, as these are found
from the points either side of the connection. Figure 9 shows the concatenation
of two quadratic Bezier curves to form a Bezier spline, while figure 10 shows
two cubic Bezier curves joined into a Bezier spline representation of an aerofoil.
The Python code to produce the spline in this figure is included below. By
(2)
(1)
(1)
(2)
changing six parameters: py , qy , px&y , qx&y , a good degree of shape control
is possible. Further control can be had by manipulating the weights, z (i) , and
we will consider this later.

1.3

Lab 1: create a B
ezier spline using Python

Create a file aclab1.py that contains the functions listed below.


The binom function in listing 2.
10

q (2 )

p (1 )

p (2 ) = q (0 )

q (1 )

p (0 )

Figure 9: an example of a Bezier spline formed by joining two quadratic Bezier


curves.

q (2 )
q
p

(3 )

=q

(1 )

(0 )

p (1 )

p (0 ), q (3 )

p (2 )

Figure 10: an example of a Bezier spline representation of an aerofoil, formed


by joining two cubic Bezier curves.

11

Figure 11: Example output from bezier(points)


A function bezier(points) that creates a cubic Bezier curve with control
points defined by an n 2 array points, returns a 101 2 array of x, y
points on the curve for t[0, 1] and plots the curve, control points and
control polygon.
Example:
>>> p o i n t s=a r r a y ( [ [ 0 , 0 ] , [ 1 , 0 . 5 ] , [ 0 . 5 , 1 ] , [ 0 . 5 , 2 ] , [ 0 ,
>>> p r i n t b e z i e r ( p o i n t s )

1.5]])

[ [ 0 . 0. ] [ 0.03910797 0.02000198]
...
[ 0.01970397 1.51910798] [ 0 . 1.5 ] ]

You can tackle this code writing problem by modifying the code in listing
1, which implements the last line of equation 15. Instead of explicitly
coding this cubic Bezier equation, you first need to code it as a sum of
four terms using the binom function, i.e. as in the second line of equation
15. Use a for loop followed by sum to do this. Next make you code handle
any number of control points by using n+1 instead of four terms. Finally,
make points an input to the function, rather than have the control points
hard-coded as in listing1 (where the array a is the control points).
A function rational bezier(points, weights) that creates a rational
cubic Bezier, with n control points with n weights in the input arrays
points and weights, returns a 1012 array of x, y points on the curve for
t[0, 1] and plots the curve, control points and control polygon. Example:
>>> p o i n t s = a r r a y ( [ [ 0 , 0 ] , [ 1 , 0 . 5 ] , [ 0 . 5 , 1 ] , [ 0 . 5 , 2 ] , [ 0 ,
>>> w e i g h t s = a r r a y ( [ 1 , 1 0 , 5 , 1 5 , 1 ] )
>>> p r i n t r a t i o n a l b e z i e r ( p o i n t s , w e i g h t s )

12

1.5]])

Figure 12: Example output from rational bezier(points, weights)


[[
[
...
[
[

0.
0.28824076

0.
]
0.14582872]

0.18929222
0.

1.68733879]
1.5
]]

This function only requires minor modifications to your bezier(points)


code. Refer to equations 16 and 17.

Computational analysis of geometry

2.1

Lecture 3: XFOIL (an example of computational analysis)

XFOIL is an interactive program for the design and analysis of subsonic isolated
airfoils [Drela, 1989]. It combines the speed and accuracy of high-order panel
methods with a fully-coupled viscous boundary layer method [Drela and Giles,
1987]. Here we will consider XFOIL as a black-box analysis method and use it to
understand geometry manipulation, linking computer codes, and optimisation,
i.e. we will use it, but not try to understand it (that would be part of a
aerodynamics and computational fluid dynamics module).
XFOIL can be found under Your School Software. Selecting XfoilP4
opens the DOS command prompt and the program can be run interactively
using a set of commands detailed in the user guide. A sample XFOIL session can
be found at http://web.mit.edu/drela/Public/web/xfoil/. Listing 3 is a sample
session that runs a viscous analysis of an aerofoil defined in aerofoil.dat. The
inputs work, line-by-line as follows:
load aerofoil coordinates form the aerofoil.dat file,
name this aerofoil CubicBez,
13

smooth the aerofoil using the panel command,


enter the analysis routine using the oper command,
define a viscous analysis at a Reynolds number of 1.4 106 ,
set the Mach number to 0.1,
type 1 means that the Mach number, Reynolds number, aerofoil chord
and velocity will be fixed and the lift will vary (other types of analysis are
described in the XFOIL user guide, section 5.7),
store data in a file using the pacc command (short for polar accumulate),
set polar.dat as the file in which to store data,
return to ignore a prompt at this point,
iterate the analysis 1000 times,
set a target lift coefficient for the analysis of 1.2 (i.e. the angle of attack
will be varied automatically to obtain this lift coefficient),
return to come out of opera menu, and
quit XFOIL.
Instead of entering these commands directly into the XFOIL prompt, the
program can be run in batch by saving the commands in listing 3 in a file called,
say, commands.in and typing XFOIL PATH\XfoilP4 < YOUR PATH\commands.in
at the DOS prompt (replace \ with / for LINUX/Mac operating systems).
Depending on the operating syste,, XFOIL requires a slightly dierent input file
format: listing 3 is for Windows and listing 4 is for OSX.
Listing 3: Sample Xfoil input file
l o a d C: \ U s e r s \ a i j f 1 9 7 \ Documents \ python \ a e r o f o i l . dat
CubicBez
panel
oper
v i s c 1397535
M 0.1
type 1
pacc
C: \ U s e r s \ a i j f 1 9 7 \ Documents \ python \ p o l a r . dat
iter
5000
cl 1.2

quit

14

Listing 4: Sample Xfoil input file for OSX


l o a d / U s e r s / a l e x / Dropbox / Teaching / PiNotebook / a e r o f o i l . dat
CubicBez
panel
oper
v i s c 1397535
M 0.1
type 1
pacc
/ U s e r s / a l e x / Dropbox / Teaching / PiNotebook / p o l a r . dat
i t e r 5000
cl 1.2

quit

Listing 5 is an example of the polar.dat output file. The format of this file
is always the same, which is useful to know if you are going to interrogate it for
data. If the output file entered in XFOIL already exists, the pacc command in
listing 3 will fail. It is therefore best to use a new output file name or delete the
existing output file before XFOIL is run again.
Listing 5: sample Xfoil output file.
XFOIL

Version 6.97

C a l c u l a t e d p o l a r f o r : CubicBez
1 1 Reynolds number f i x e d
xtrf =
Mach =
alpha
3.699

2.1.1

1 . 0 0 0 ( top )
0.100
Re =
CL
1.2000

CD
0.00745

Mach number f i x e d
1 . 0 0 0 ( bottom )
1.398 e 6
Ncrit =
CDp
0.00290

CM

9.000

Top Xtr

Bot Xtr

0.4208

1.0000

0.1629

Creating the .dat and .in files using Python

The aerofoil.dat file contains the x,y coordinates of the aerofoil in two columns
(x and y), with the coordinates starting at the trailing edge, running along the
lower surface to the leading edge, and back along the upper surface to the trailing edge. Assuming the coordinates are stored in the (1011) arrays lower and
upper, we can write these aerofoil coordinates in two ways: line-by-line using
the write function (listing 6) or, more succinctly, using the savetxt function
(listing 7). Note that in both cases the last element of the lower array is omitted
so that the leading edge point is not duplicated.
Listing 6: line-by-line writing of aerofoil coordinates.
d a t a f i l e = open ( f i l e p a t h + a e r o f o i l . dat , w )

15

f o r i in r a n g e ( 0 , 1 0 0 ) :
d a t a f i l e . w r i t e ( %f %f \ r %( l o w e r [ i , 0 ] , l o w e r [ i ,
#LINUX/OSX: d a t a f i l e . w r i t e (% f \ t%f \n %( l o w e r [ i , 0 ] ,
f o r i in r a n g e ( 0 , 1 0 1 ) :
d a t a f i l e . w r i t e ( %f %f \ r %(upper [ i , 0 ] , upper [ i ,
#LINUX/OSX: d a t a f i l e . w r i t e (% f \ t%f \n %(upper [ i , 0 ] ,
data file . close ()

1]))
lower [ i , 1 ] ) )
1]))
upper [ i , 1 ] ) )

Listing 7: writing aerofoil coordinates with savetxt.


s a v e t x t ( f i l e p a t h + a e r o f o i l . dat , v s t a c k ( [ l o w e r [ 0 :

1 ] , upper ] ) )

While with the aerofoil.dat file we are writing columns of numbers from
which the shape of the aerofoil is not immediately apparent, the input file is
readable (at least to the trained eye) and we wish to preserve this in the Python
code via which we write it. In listing 8 the write function is used in a format
where the structure of the resulting file is apparent and the variable names, e.g.
Re for Reynolds number, are logical. Writing code in this way is not particularly
succinct, but is often preferable from a development perspective.
Listing 8: writing the commands.in file in Python in an easily identifiable format.
c o m m a n d f i l e=open ( f i l e p a t h + commands . i n , w )
c o m m a n d f i l e . w r i t e ( l o a d + f i l e p a t h + a e r o f o i l . dat \n\
CubicBez \n\
p a n e l \n\
o p e r \n\
v i s c + s t r ( Re ) + \n\
M + s t r (M) + \n\
t y p e 1\n\
pacc \n \
+ f i l e p a t h + p o l a r . dat \n\
\n\
i t e r \n 1000\ n\
c l 1 . 2 \ n\
\n\
\n\
q u i t \n )
command file . c l o s e ( )

2.1.2

Running XFOIL from Python

System calls can be made from Python after importing os using os.system().
For example, assuming XfoilP4 is in C:\apps\XFOIL\ and commands.in and
aerofoil.dat are in C:\Users\aijf197\Documents\, an output similar to listing 5 could be produced with the code in listing 9. Note the use of double
backslashes in the file paths. This is because \ is a special character in Python
and needs to be escaped by adding the extra backslash. XFOIL needs to be able
to write to the directory defined by your file path. This might not be possible
for remote/shared drives, so it may be necessary to work in a local directory,
e.g. use the Documents folder and not My Documents. However,

16

make sure you copy your work across to My Documents or backup in some other way before you logout. Note that to run XFOIL using
os.system() on one of the University workstations, you need to first open and
close XFOIL from the start menu (you only need to do this once each time you
login to a new machine).
Listing 9: sample Python code to run XFOIL
import o s
f i l e p a t h = C: \ \ U s e r s \\ a i j f 1 9 7 \\ Documents \\
x f o i l p a t h = C: \ \ apps \\XFOIL\\
r u n x f o i l c o m m a n d = x f o i l p a t h + XFOIL < + f i l e p a t h + \
commands . i n
o s . system ( r u n x f o i l c o m m a n d )

To avoid worrying about operating systems and entering \ or /, the os.path.sep


function can be used to re-write listing 9 as shown in listing 10.
Listing 10: sample Python code to run XFOIL with os.path.sep command
import o s
s e p=o s . path . s e p
f i l e p a t h = C : + s e p + U s e r s + s e p + a i j f 1 9 7 + s e p + \
Documents + s e p
x f o i l p a t h = C : + s e p + apps + s e p + XFOIL + s e p
run xfoil command = x f o i l p a t h + x f o i l < + f i l e p a t h + \
commands . i n
o s . system ( r u n x f o i l c o m m a n d )

Now we need to retrieve the results from polar.dat (listing 5), which can be
read using, for example, the Python readlines() function as shown in listing
11. Here we read all the lines of the file and then assign specific elements of the
last line to cl and cd. A command to delete the polar.dat file is included to
allow XFOIL to be run again in the same way if needed.
Listing 11: sample Python code to read results from polar.dat
a e r o d a t a f i l e = open ( f i l e p a t h + p o l a r . dat , r )
lines = aero data file . readlines ()
aero data file . close ()
#d e l e t e X f o i l o u t p u t f i l e r e a d y f o r n e x t X f o i l run
o s . system ( d e l + f i l e p a t h + p o l a r . dat )
#Linux /OSX: os . s yst e m ( rm f + f i l e p a t h + p o l a r . d a t )
cl = float ( lines [ 1][11: 17])
cd = f l o a t ( l i n e s [ 1 ] [ 2 0 : 2 7 ] )

2.2

Lab 2: run an XFOIL analysis of a B


ezier spline aerofoil using Python

Create a file aclab2.py that contains the functions listed below.

17

A function bezier spline aerofoil(file path) that creates a Bezier


spline aerofoil with lower curve control points:
l(0)

(1.0, 0.0)

l(1)
l(2)

=
=

(0.5, 0.08)
(0.0, 0.05),

(20)

and upper curve control points:


u(1)

(0.0, 0.1)

u(2)

(0.4, 0.2)

u(3)

(1.0, 0.0).
(0,1,2,3)

(21)

(0,1,2,3)

The weighting of all control points is 1, i.e. zl


= 1 and zu
= 1.
The function should print aerofoil coordinates to the file aerofoil.dat
in two columns. The first column is the x-coordinates, starting from the
trailing edge (x = 1) with 101 points along the lower surface to the leading
edge (x = 0) and then 100 points alone the upper surface to the trailing
edge (x = 1). The second column contains 201 y-coordinates from the
trailing edge (y = 0) to the leading edge (y = 0) and back to the trailing
edge (y = 0).
Example:
1.000000
0.985001
...
0.000150
0.000000
0.000120
...
0.982060
1.000000

0.000000
0.002337
0.022811
0.025000
0.027257
0.005910
0.000000

You already have the function rational bezier from aclab1.py to create
the upper and lower Bezier curves, but you need to find the leading edge
control point l(3) = u(0) by coding equation 19. With this point calculated,
rational bezier can be called to calculate the points on the upper and
lower curves and then these can be written to aerofoil.dat using, e.g.
listing 6.
A function run xfoil(file path, xfoil path) that runs XFOIL, reading in aerofoil.dat created by bezier spline aerofoil() and running
the viscous analysis using the commands in listing 3, reads and returns
CD and CL from polar.dat.
Example:
>>> p r i n t r u n x f o i l ( f i l e p a t h , x f o i l p a t h )
(0.00802 , 1.2)

18

Listings 8, 10 and 11, which will form the basis of your code, are provided
in a text file on Blackboard for your convenience.
If your function is not working, first try opening XFOIL from the start
menu and copy and paste the contents of commands.in (opened in notepad)
into the XFOIL command prompt line-by-line to check that your input
file is formatted correctly. Next check that polar.dat has been created
and contains Cl and CD values.

3
3.1

Optimization
Lecture 4: shape optimization

All areas of design are concerned with producing the best product in terms of
physical performance, cost, aesthetics, etc. Engineering design is, more formally,
the optimisation of performance criteria based on analyses. The performance
criteria will include profit margin and various fitness criteria such as weight,
stiness, cost to consumer, etc. The analyses could include cost modelling,
finite element analysis, wind-tunnel testing, consumer trials, etc. Indeed, a
major part of engineering design is identifying appropriate criteria and analyses
on which to base decisions.
We are going to design an aerofoil section for SUHPA (Southampton University Human Powered Aircraft, figure 13). We will conisider the section for the
six metre station (six metres out from the wing root). Here the chord is 0.698
m and the design speed for this aerofoil will be 12.5 m/s. Thus the constants
defined at the top of our functions that run XFOIL will look like:
a=340.3 #speed of sound
v=12.5 #velocity
M=v/a #Mach number
nu=0.00001461 #kinematic viscosity
L=0.698 #chord length of aircraft wing
Re=v*L/nu #Reynolds number
We will also need to define the lift coefficient, which at this location along
the wing is 0.843.
We will minimise the drag (known as the cost function, or objective function)
of an aerofoil for a fixed lift (our constraint function). We have one form of analysis: XFOIL. The optimum aerofoil is found through methodical changes to its
geometry based results from XFOIL according to some optimisation algorithm.
There are many optimisation algorithms. Indeed whole scientific communities
are devoted to their development. Algorithms include local methods such as
the Newton-Raphson method (which steps towards a local minimum, or saddle point, based on first and second derivatives of the objective function) and
quasi-Newton methods (e.g. Broyden-Fletcher-Goldfarb-Shanno, BFGS, where
second derivatives are not required). Global methods include those inspired by
nature like genetic algorithms and particle swarm. Each of these algorithms is
19

Figure 13: SUHPA in flight during the 2013 Icarus Cup at Sywell Aerodrome
(piloted by Guy Martin, photo by Fred To).
suited to dierent types of problems. We will examine the use of the Python
implementations of BFGS.
3.1.1

BFGS

SciPys scipy.optimize.fmin bfgs minimises a function using the BFGS algorithm. Information from the SciPy reference guide on the required input
parameters and outputs is given below.
The algorithm is called with:
scipy.optimize.fmin bfgs(f, x0, fprime=None, args=(), gtol=1e-05, norm=inf,
epsilon=1.4901161193847656e-08, maxiter=None, full output=0, disp=1,
retall=0, callback=None),
where the input parameters are:
f : callable f(x,*args). Objective function to be minimized.
x0 : ndarray. Initial guess.
fprime : callable f(x,*args), optional. Gradient of f.
args : tuple, optional. Extra arguments passed to f and fprime.
gtol : float, optional. Gradient norm must be less than gtol before successful termination.
norm : float, optional. Order of norm (Inf is max, -Inf is min)
epsilon : int or ndarray, optional. If fprime is approximated, use this value
for the step size.

20

callback : callable, optional An optional user-supplied function to call after


each iteration. Called as callback(xk), where xk is the current parameter
vector.
maxiter : int. Maximum number of iterations to perform.
full output : bool. If True,return fopt, func calls, grad calls, and
warnflag in addition to xopt.
disp : bool Print convergence message if True.
retall : bool. Return a list of results at each iteration if True.
and returns:
xopt : ndarray. Parameters which minimize f, i.e. f(xopt) == fopt.
fopt : float. Minimum value.
gopt : ndarray. Value of gradient at minimum, f(xopt), which should be
near 0.
Bopt : ndarray. Value of 1/f(xopt), i.e. the inverse hessian matrix.
func calls : int. Number of function calls made.
grad calls : int. Number of gradient calls made.
warnflag : integer 1 : Maximum number of iterations exceeded. 2 : Gradient and/or function calls not changing.
allvecs : list. Results at each iteration. Only returned if retall is True.
The call statement above contains the default values for the parameters.
We only need to specify parameters that require dierent values. Naturally f
(the objective function) must be specified, along with a starting point for the
optimiser, x0. XFOIL does not provide gradient information, so fprime is left
at None. We can pass arguments such as cl (the lift coefficient), file path and
xfoil path to the objective function using the args parameter. The algorithm
will stop when either gtol or maxiter is reached. By default gtol=1e-05 is
used, i.e. the algorithm iterates until the gradient of the objective function with
respect to the variables is below 1 10 5 . It computational resources and/or
time are limited, maxiter can be specified too.
The other parameter we need to worry about is epsilon. With fprime=None,
the BFGS method approximates the gradient of the function with respect to the
design variables using finite dierencing. The step size used for this finite dierencing (epsilon) is critical. For analytical functions, a smaller step size gives
better precision (but not so small that machine precision is an issue). For iterative solutions using computational meshes (e.g. XFOIL), the noise in the
function means that small step sizes will be inaccurate and may even yield gradients in the wrong direction. A balance between a large enough step to avoid
noise issues and a small enough step size to yield a useful gradient is required.
3.1.2

L-BFGS-B

Here the L stands for limited memory, which means that this algorithm is
more efficient for large numbers of design variables and, more important to us,
21

the B stands for bounded, which means that bounds can be set on the design
variables. This is important as often geometry becomes physically unrealisable
or unsolvable outside of certain bounds on the design variables, resulting in simulation failures and so optimiser failure. SciPys scipy.optimize.fmin l bfgs b
is called in a similar way to scipy.optimize.fmin bfgs (or a maddeningly
slightly dierent way, depending on how you see things):
scipy.optimize.fmin l bfgs b(f, x0, fprime=None, args=(), approx grad=0,
bounds=None, m=10, factr=10000000.0, gtol=1e-05, epsilon=1e-08, iprint=-1,
maxfun=15000, disp=None)
The new parameters that concern us are:
approx grad : bool. Whether to approximate the gradient numerically (in
which case func returns only the function value).
bounds : list. (min, max) pairs for each element in x, defining the bounds
on that parameter. Use None for one of min or max when there is no bound in
that direction.
3.1.3

Further considerations

As alluded to above, noise in the objective function may cause difficulties


when calculating gradients. As a subsonic aerofoil geometry variable changes,
one would expect a smooth, continuous change in the lift and drag. However,
as the flow is being solved over a finite computational mesh, changes in the
geometry and subsequent re-meshing will result in small step-changes in the
solution. Figure 14 shows a set of aerofoils and the XFOIL predicted CD for
these airfoils. Note the scatter around an assumed smooth trend. The BFGS
epsilon will need to be selected carefully for the optimiser to succeed here.
Sometimes it is preferable to fit a curve (or surface for multiple variables) to
the data and optimise using the curve-fit in lieu of the true objective function.
This is quicker and removes problems with noise. Listing 12 shows the Python
code to produce the data and moving least-squares curve fit in figure 14.
Listing 12: Python code to produce a moving least squares fit to the XFOIL
data.
from s c i p y . o p t i m i z e import f s o l v e
#1D q u a d r a t i c moving l e a s t s q u a r e s
def mls ( x , X, y , sigma ) :
N = max( shape ( y ) )
w e i g h t s = z e r o s (N)
A = z e r o s ( [ N, 3 ] )
A [ : , 0 ] = X2
A[ : , 1] = X
A[ : , 2 ] = ones ( [ 1 , N] )
f o r i in r a n g e ( 1 , N ) :
w e i g h t s [ i ] = exp( sum ( ( x
X[ i ] ) 2 ) / ( 2 sigma ) )
W = diag ( weights )

22

a = l i n a l g . l s t s q ( dot ( dot (A. c o n j ( ) . T, W) , A) , \


dot ( dot (A. c o n j ( ) . T, W) , y ) )
f = a [ 0 ] [ 0 ] x 2 + a [ 0 ] [ 1 ] x + a [ 0 ] [ 2 ]
return f
#1D q u a d r a t i c moving l e a s t s q u a r e s c r o s s v a l i d a t i o n
def m l s e r r o r ( sigma , X, y ) :
y test = zeros (11)
error = zeros (11)
f o r i in r a n g e ( 0 , 1 1 ) :
y t e s t [ i ] = mls (X[ i ] , append (X [ 0 : i ] , X[ i + 1 :
append ( y [ 0 : i ] , y [ i + 1 :
1 ] ) , sigma )
e r r o r [ i ] = ( cd [ i ]
y t e s t [ i ] ) 2
s u m e r r o r = sum ( e r r o r )
return s u m e r r o r

1]) ,\

file path = . . .
xfoil path = . . .
w array = l i n s p a c e ( 0 . 6 , 1 . 2 , 11)
figure (1)
cd = p a r a m e t e r s w e e p ( w array , 0 . 8 4 3 , f i l e p a t h , x f o i l p a t h )
s a v e f i g ( a e r o f o i l s . pdf )
# f i t moving l e a s t s q u a r e s
s i g m a b e s t = f s o l v e ( m l s e r r o r , 0 . 2 , a r g s = ( w array , cd ) )
w f i n e = l i n s p a c e ( 1 . 1 5 , 1 . 8 5 , 101)
y pred = zeros (101)
f o r i in r a n g e ( 0 , 1 0 1 ) :
y p r e d [ i ] = mls ( w f i n e [ i ] , w array , cd , s i g m a b e s t )
figure (2)
p y l a b . p l o t ( w array , cd , o , l a b e l = XFOIL data )
p l o t ( w f i n e , y p r e d , l a b e l = MLS f i t )
legend ()
x l a b e l ( w )
ylabel ( c d )
s a v e f i g ( parameter sweep . pdf )

3.2

Lab 3: conduct an aerofoil shape parameter study


with Python and XFOIL

Create a file aclab3.py that contains the functions listed below.


A function parametric aerofoil(w,file path) that creates a Bezier
(2)
spline aerofoil with the same control points as Lab 2, but with zu = w
(w being defined by w; the first input argument of the function. As in Lab
2, this function should write coordinates to the file aerofoil.dat and in
the same format as for Lab 2.
A function run xfoil wcl(w,cl,file path,xfoil path) that runs XFOIL,
reading in aerofoil.dat created by parametric aerofoil() and running the viscous analysis using the commands in listing 3 (except that the
lift coefficient in commands.in should now be defined by cl), reads and
23

Figure 14: aerofoil shapes (note scales are not equal) and a moving least squares
for to the XFOIL data, calculated in listing 12.

24

returns CD and CL from polar.dat. Use the SUHPA velocity and aerofoil
chord values.
Example:
>>> p r i n t r u n x f o i l w c l ( 1 . 1 , 0 . 8 4 3 3 , f i l e p a t h , x f o i l p a t h )
(0.00738 , 0.843)

A function parameter sweep(w array,cl,file path,xfoil path) which


is the same as run xfoil wcl(), but where w array is an array of weights
for the control point at [0.4, 0.2]. The function should return the corresponding array of CD values.
Example:
>>> w a r r a y = l i n s p a c e ( 0 . 6 , 1 . 2 , 1 1 )
>>> p r i n t p a r a m e t e r s w e e p ( w array , 0 . 8 4 3 , f i l e p a t h , x f o i l p a t h )
[ 0.00744
0.00721
0.00733

0.00697

0.00669

0.00746

0.00759]

0.00655

0.0065

0.00681

0.00706

Add the code in lising 12 to plot a moving least squares fit to your XFOIL
data:

3.3

Lab4: optimise an aerofoil using Python and XFOIL

Create a file aclab4.py that contains the functions listed below.


A function one dim opt(x0,cl,file path,xfoil path) which optimises
(2)
the Bezier spline aerofoil weight zu for SUHPA using the scipy.optimize.fmin bfgs
optimiser.
Use lower curve control points:
l(0)

(1.0, 0.0)

(1)

(0.5, 0.08)

l(2)

(0.0, 0.05),

(22)

and upper curve control points:


u(1)

(0.0, 0.1)

u(2)

(0.4, 0.2)

u(3)

(1.0, 0.0).

Example:
>>> p r i n t o n e d i m o p t ( x0 , c l , f i l e p a t h , x f o i l p a t h )

Optimization terminated s u c c e s s f u l l y .

25

(23)

Current f u n c t i o n value : 0.006490


Iterations : 2
F u n c t i o n e v a l u a t i o n s : 24
Gradient e v a l u a t i o n s : 8
[ 0.85011615]

The scipy.optimize.fmin bfgs optimiser is called in the format:


>>> o p t o u t=f m i n b f g s ( r u n x f o i l w c l , x0 , a r g s =( c l , f i l e p a t h , x f o i l p a t h ) )

where x0 is the starting point for the optimiser, i.e. your best guess at
(2)
the optimum zu .
A function four dim opt(x0,weight limits,cl,file path,xfoil path)
(2) (3,) (2) (3)
which optimises the Bezier spline aerofoil weights zu , zu , zl , zl for
SUHPA using the scipy.optimize.fmin l bfgs b optimiser. Use the
above control points.
Example:
>>> p r i n t f o u r d i m o p t ( x0 , w e i g h t l i m i t s , c l , f i l e p a t h , x f o i l p a t h )
( array ( [ 1.3 ,

0.5 ,

0.5 ,

0 . 5 ] ) , 10 , 0)

The scipy.optimize.fmin l bfgs b optimiser is called in the format:


>>> o p t o u t=f m i n l b f g s b ( r u n x f o i l w c l , x0 , a r g s =( c l , f i l e p a t h , x f o i l p a t h ) ,
bounds=w e i g h t l i m i t s , e p s i l o n=s t e p s i z e , a p p r o x g r a d=True )

where x0 is the starting point for the optimiser, e.g. x0=[1, 1, 1, 1]


, weight limits are upper and lower bounds on the the weights to be optimised,1 e.g. weight limits=((0.5, 1.3),(0.5, 1.3),(0.5, 1.3),(0.5,
1.3)) and step size is the finite dierencing step size (the same as for
scipy.optimize.fmin bfgs). The input approx grad=True is required
so that step size can be defined.
This lab will be assessed during next weeks lab session.

Further computational geometry

The material in this section will not be assessed, but is included here to show
how your Python implemented geometry sits in context with the NURBS based
geometry of modern CAD engines.
1 If no upper and lower bounds are set, the optimiser may try to evaluate geometries that
cannot be solved in XFOIL. Wide bounds increase the search-space and so better designs
could be found, but failures are more likely. If XFOIL fails, shrink your bounds. Look at the
optimised values (stored in opt) to see if upper or lower bounds are being hit and expand your
bounds accordingly

26

a (0 )

a (1 )
a (3 )

a (4 )

a (2 )

Figure 15: a quadratic rational B-spline showing the vicinity of local control as
control point a(0) is shifted.

4.1

Lecture 5: B-splines and NURBS

B-splines, where the B stands for basis, have a number of key characteristics
which may make them preferable to Bezier splines in many applications.
The degree of a B-spline is not determined by the number of control points.
Local control of the spline is possible (unlike Bezier splines where moving
one control point aects the whole spline).
The degree of continuity between segments can be specified.
Local control of the spline means that moving or changing the weight of a
control point will only aect the spline in the vicinity of this point. The extent
of this vicinity is determined by the degree of the B-spline. In figure 15 we
see that moving control point a(0) only aects the a degree two spline up until
midway between a(1) and a(3) (up until the end of the first segment).
A B-spline is made of n d segments, which we shall call B0 , B1 , ..., B(n d) ,
where n + 1 is the number of control points (remember these are numbered
from zero) and d is the degree of each segment (the degree of the B-spline).
The segments start and end at joints k(0) , k(1) , ..., k(n d+1) . Note that, unlike
a Bezier spline, the ends of the B-spline k(0) and k(n d+1) do not coincide with
the first and last control points. The exception to this is if there are multiple
control points: a degree d B-spline passes through a control point repeated d
times. For example if a(0) = a(1) , a degree 2 B-spline will start at the first
control point (the first segment will be a straight line towards a(2) ).
We will start by defining a uniform B-spline using control points and then
go on to interpolating B-splines, i.e. defined by the joint points, which may be
more practical in some situations.
27

Expressed in the same way as (??), the ith segment of a d-degree uniform
B-spline can be calculated as
0
1
a(i 1)
B
C
a(i)
B
C
Bi (t) = (td , td 1 , ..., t, 1)M(d) B
(24)
C
..
@
A
.
a(i+d 1)
where elements of the basis matrix M are given by
(d)

mi,j =

1
d!

n
i

X
d

(d

k)i ( 1)d

1
1

1
0

k=j

d+1
k j

(25)

The first four M matrices are:

M(d=1)
M(d=2)

M(d=3)

M(d=4)

=
=

1
1!

1
1 @
2
2!
1
0
1
1 B
B 3
3! @ 3
1
0
1
B 4
B
1 B
6
4! B
@ 4
1

2
20
1

,
1
0

A,

3
6
0
4

3
3
3
1

4
12
6
12
11

6
12
6
12
11

1
1
0 C
C,
0 A
0

1
4 1
4 0 C
C
6 0 C
C.
4 0 A
1 0

Varying t from zero to one, equation 24 traces out the ith segment of the
B-spline between joints k(i) and k(i+1) . These joints can be calculated as
0 (d) 1
c0
B (d) C
c1 C
B
1
k(i) = (a(i) a(1) . . . a(i+d 1) ) B
(26)
. C
B
C
d!
@ .. A
(d)

cd

where

(d)

cj

d
X

(d

k)d ( 1)k

k=j

(d)

d+1
k j

(c0,1,...,d is also the first d elements of the last row of the M(d) matrix).
28

(27)

4.1.1

interpolating B-spline

Having found the joint points, given the control points, we can now find the
control points (and so the B-spline) given the joint points. We actually need
a little more information. A B-spline with n + 1 control points has n d + 1
joints and so along the n d + 1 equations for these joints, we need d 1
further equations. Specifying tangents gives us the remaining expressions. For
a quadratic B-spline we only need to specify one tangent, e.g. the start tangent,
resulting the the following system of equations:
0

B c(2)
B 0
1 B
B 0
2! B
B ..
@ .
0

2
(2)
c1
(2)
c0

..
.
0

0
0
(2)
c1

..
.
0

...
...
...
..
.

0
0
0
..
.

0
0
0
..
.

...

c0

(2)

c1

(2)

10

a(0)
C B (1)
CB a
C B (2)
CB a
CB .
C@ .
.
A
a(n)

C B
C B
C B
C=B
C B
A @

t(0)
k(0)
k(1)
..
.
k(n

d+1)

C
C
C
C,
C
A

(28)

which can easily be solve to find a(0,...,n) .


We can use the above to define a B-spline through a set of joints; yielding
a versatile curve with intuitive,local control via the calculated control points.
However, specifying a mix of joints and tangents may be undesirable when
compared with the convenient method of defining a Bezier curve with start, end
and intermediate control points. An answer lies in the use of a knot vector.
4.1.2

Knots

Each segment of a B-Spline is defined over an interval where t varies 2 [0, 1]


and the curve is defined, in this interval by d + 1 control points. For example,
from equation 24 we see the first segment of a degree 3 B-spline is defined
by a(0) , a(1) , ..., a(3) multiplied by some weightings (basis functions; the B in
B-spline). These basis functions overlap from segment to segment. Figure 16
shows how four basis functions are in play in each interval for a degree three
B-spline. Referring back to equation 24, in each interval a(i 1) is multiplied by
the basis function decreasing from 16 , a(i) by the one decreasing from 23 , a(i+1)
by the one increasing from 16 , and a(i+2) by the one increasing from 0. Note
how similar these basis functions are to Bezier curve weightings, except that,
since they overlap, the curve does not pass through any control points.
The start and end of the intervals can be defined as a sequence of knots in a
non-decreasing vector with unit, uniform spacing (e.g k = [ 10123], as in figure
16). The parameter t varies 2 [0, 1] between each of these knots. In practice, we
work with the knot-vector when defining the B-spline and convert to t 2 [0, 1]
in the maths. The power of this knot-vector definition is when we make it
non-uniform, with repeated values and non-unit intervals. This produces a nonuniform B-spline and we only need to make it rational to create a NURBS. All
we need to do is multiply by control point weights and divide through by the

29

basis f unc t ions

2/3

1/6

0
1

k not value s

Figure 16: Overlapping degree three B-spline basis functions.


sum of the product of these weights and the basis function (in the same way as
equation 17) and we have a non-uniform rational basis spline (NURBS).
4.1.3

NURBS

By varying the knot vector in a non-uniform manner, we can achieve an increased level of versatility and recoup some of the attractive features of B/ezier
curves. The first feature to note is that repeating a knot value d+1 times results
in the NURBS passing through a control points. The knot vector is n + d + 1
long so for a degree three NURBS with four control points there are eight knots.
Repeating the first and last knot values four times will create a NURBS with
one cubic segment that starts and ends at the first and last control point. The
basis functions for this NURBS are shown in figure 17. We see that they are the
same as those for a Bezier curve (figure 6). A Bezier curve is indeed a special
case of a NURBS from which, using the knot vector and the ability to specify
the degree of the NURBS, we can add more and more complexity.
We can take advantage of NURBS local control to, for example, refine the
shape of the B/ezier curve aerofoil definition in figure 10. With the influence of
a control point extending over d + 1 knot values, adding an extra control point
will not give local control in our degree three definition. Reducing to degree
two and adding a control point gives local control, as shown in figure 18 where
moving a control point on the lower surface gives control over the aft portion of
the aerofoil without aecting the leading edge region.
The knot vector for the lower surface NURBS is k = [0, 0, 0, 1, 2, 3, 3, 3] and
the resulting basis functions are shown in figure 19. Note how the second basis
function is non-zero only for the first and second segment, i.e. it has no influence
on the last segment (the leading edge region of the aerofoil in figure 18.
30

0.9

basis f unc t ion value

0.8

0.7

0.6

0.5

0.4

0.3

0.2

0.1

0
0000

1111

k not value s

Figure 17: Repeated knot values in a third degree NUBRS yield the cubic
Bernstein polynomials in figure 6 (i.e. the NURBS is equivalent to a B/ezier
curve).

k (3) = 1

k ( 5 , 6 , 7 )= 3

k ( 0 , 1 , 2 )= 0
k

(4)

=2

Figure 18: Reducing the degree of the lower surface NURBS to three and adding
a control point gives local control towards the trailing edge, potentially giving
more design capability than the B/ezier curve definition in figure 10 (shown
here by moving the additional control point). Note that the plot has been
stretched to highlight the geometry changes.

31

0.9

basis f unc t ion value

0.8

0.7

0.6

0.5

0.4

0.3

0.2

0.1

0
000

333

k not value s

Figure 19: Degree two NURBS basis functions for knot vector k =
[0, 0, 0, 1, 2, 3, 3, 3], resulting in a curve starting and ending at the first and last
control points. The central segment is uniform, but the first and last segments
have basis functions distorted by the repeated knot values.
A further level of detail can be obtained by varying the spacing of the knot
vector. Bringing knots together pulls the NURBS towards the control polygon:
for a degree three NURBS two repeated knot values matches the gradient of
the NURBS at the knot to the control polygon (i.e. alligns it with a line drawn
between the knots either side), three repeated knot values causes the NURBS
to pass through a control point (with a gradient discontinuity), four repeated
knots causes a break in the NURBS between two control points.
Figure 20 shows the range of manoeuvres possible with a degree three
NURBS (omitting the cause of changing control point weights, which is similar
to changin rational B/ezier spline weights, as shown in figure 12). Figure 20(a)
is a uniform degree threevB-spline and the uniform weightings from the uniform
knot-vector are shown in the right hand plot. Figure 20(b) has the degree
increased to seven and the first and last knot values repeated to yield a Bezier
spline equivalent. In figure 20(c) the degree is reduced back to three and the
first and last knots repeated such that the NURBS starts and ends at the first
and last control points. Figure 20(c) to (f) show the eect of repeated knot
values: causing tangent matching, control point interpolation, and breaks in
the NURBS.
As a weighted sum of control points, the equation for a NURBS can be
expressed in a similar way as a Bezier curve:
N (t) = sumni=0 a(i) Bi,d (t).

(29)

However, since the basis functions Bi,d (t) depend on the knot vector, the calculation of Bi,d (t) is rather tedious and usually performed recursively. That
32

(a)
k

( 3)

= 3

( 4)

= 4

( 7)

= 7k

( 5)

= 5

( 8)

= 8

( 6)

= 6

10

11

( b)

( 0, . . . , 7)

= 0

( 8, . . . , 15)

= 1

00000000

11111111

( c)
k

( 4)

= 1

k
k

( 5)

= 2

( 07)

= 4

( 6)

= 3

( 0, 1, 2, 3)

= 0

( 8, , 9, 10, 11)

0000

( 8, , 9, 10, 11)

0000

( 8, , 9, 10, 11)

0000

= 5

5555

( d)
k

( 4)

= 1

( 0, 1, 2, 3)

= 0

( 7)

= 3

( 5, 6)

= 2

= 4

22

4444

( e)
k

( 0, 1, 2, 3)

= 0

( 4, 5, 6)

= 2

( 7)

= 1

= 3

111

3333

(f )

0.5

( 0, 1, 2, 3)

= 0

( 4, 5, 6, 7)

= 1k

0
0000

( 8, , 9, 10, 11)

= 2

1111
k n ot v al u e s

Figure 20: Various NURBS defined by eight control points (left) and knot vectors (right): (a) degree three with uniform knot vector; (b) degree seven; (c-f)
degree three. The control point locations are chosen to be similar to Figure 7.19
in Salomon [2006].

33

2222

is, we start by calculating B0,1 (t) and B1,1 (t) from which B0,2 (t) can be calculated, and so on through to Bn,d (t). The implementation of NURBS in Python
is beyond the scope of this module, but the necessary equations are included
below.
The basis functions for d = 1 are defined as:

1, if t 2 [i, i + 1]
Bi,1 (t) =
(30)
0, otherwise
and the remaining basis functions can be calculated recursively from
Bi,d (t) =

k (i)

k (i+d 1)

k (i)

Bi,d

1 (t)

k (i+d)
k (i+d)

t
Bi+1,d
k (i+1)

1 (t),

(31)

noting that where a zero denominator occurs, the term should be evaluated as
zero.

References
M. Drela. XFOIL: An analysis and design system for low reynolds number airfoils. Conference on Low Reynolds Number Airfoil Aerodynamics, University
of Notre Dame, 1989.
M. Drela and M.B. Giles. Viscous-inviscid analysis of transonic and low reynolds
number airfoils. AIAA Journal, 25(10):13471355, October 1987.
G. Farin. NURBS: from projective geometry to practical use. Aerospace Science
Series. A K Peters, Natick, Massachusetts, second edition edition, 1999.
D. Salomon. Curves and Surfaces for Computer Graphics. Springer, New York,
2006.
I. Thomas. Selections Illustrating the History of Greek Mathematics (with English translation). Harvard University Press, Cambridge, Massachusetts, 1939.

34

You might also like