Download Introduction to Python: preliminaries

Document related concepts
no text concepts found
Transcript
Introduction to Python: preliminaries
Patrick Farrell
MMSC: Python in Scientific Computing
June 10, 2015
P. E. Farrell (Oxford)
Python 0
June 10, 2015
1/5
Rough schedule
Monday
Tuesday
Wednesday
Thursday
Friday
Morning
Afternoon
Datatypes, functions
Object orientation, debugging
SciPy, finite differences
p-Laplace, hyperelasticity
Navier–Stokes
Modules, plotting, iteration
NumPy arrays
Poisson, heat eqn
Stokes
Optimal control/KKT
P. E. Farrell (Oxford)
Python 0
June 10, 2015
2/5
Rough schedule
Monday
Tuesday
Wednesday
Thursday
Friday
Morning
Afternoon
Datatypes, functions
Object orientation, debugging
SciPy, finite differences
p-Laplace, hyperelasticity
Navier–Stokes
Modules, plotting, iteration
NumPy arrays
Poisson, heat eqn
Stokes
Optimal control/KKT
P. E. Farrell (Oxford)
Python 0
June 10, 2015
2/5
Timetable
Basic plan: 10:00 — 12:30; 14:00 — 17:00. I’ll usually be around here
after.
Exceptions: Finish at 16:00 on Friday afternoon.
P. E. Farrell (Oxford)
Python 0
June 10, 2015
3/5
Comments
I No TAs: please help each other! (Except the exercises that are
submitted via email)
I Feedback (good or bad) welcome:
[email protected]
P. E. Farrell (Oxford)
Python 0
June 10, 2015
4/5
Preview: final challenge!
MMSC: for this to count for your MSc, you have to write a final report.
Your task:
I Implement a finite element discretisation of a PDE.
I Take a discretisation and configuration from the literature, or invent
your own.
I Bonus points for: nonlinearity, coupling, mathematical interest, . . .
I Include an MMS verification.
I Submit working code and a report (≤ 20 pages) discussing the PDE
and its implementation.
I Inspiration: your summer projects; the PDE coffee table book.
I Deadline: 6 July 2015.
P. E. Farrell (Oxford)
Python 0
June 10, 2015
5/5
Introduction to Python: native datatypes
Patrick Farrell
MMSC: Python in Scientific Computing
May 6, 2015
P. E. Farrell (Oxford)
Python I
May 6, 2015
1 / 21
What is Python?
I Very high-level language
I Powerful complex data structures
I Supports imperative, functional, object-oriented programming
I Dynamic, extremely rapid development
I Free (very useful for parallel)
I Main scripting language used in large-scale scientific computing
I Beautiful, and fun . . .
P. E. Farrell (Oxford)
Python I
May 6, 2015
2 / 21
P. E. Farrell (Oxford)
Python I
May 6, 2015
3 / 21
Running Python
Option 1: run interactively
Option 2: put code in a file and run
$ ipython
In [1]: print("Hello, world")
Hello, world
$ cat hello.py
print("Hello, world")
$ python hello.py
Hello, world
(mainly for development)
(mainly for production runs)
P. E. Farrell (Oxford)
Python I
May 6, 2015
4 / 21
Interactive calculator
>>> 1 + 1
2
>>> a = 1
>>> a
1
>>> type(a)
<type ’int’>
>>> 5 % 3
2
>>> b = 17.03 + 19.85
>>> type(b)
<type ’float’>
>>> int(b)
36
P. E. Farrell (Oxford)
>>> c = 2 + 1.5j
>>> type(c)
<type ’complex’>
>>> c.imag
1.5
>>> c.conjugate()
(2-1.5j)
>>> s = "Hello, world!"
>>> type(s)
<type ’str’>
>>> len(s)
13
>>> s.split()
[’Hello,’, ’world!’]
Python I
May 6, 2015
5 / 21
Integer division
Be aware of integer division (à la C/C++):
>>>
0
>>>
>>>
0.5
>>>
0.5
1/2
# ?!?!
1.0/2
1.0/2.0
Use // to unambiguously specify integer division:
>>> 1.0//2.0
0.0
Python has a command line to make this issue a warning:
$ python -Q warn -c "print 1/2"
-c:1: DeprecationWarning: classic int division
P. E. Farrell (Oxford)
Python I
May 6, 2015
6 / 21
Integer promotion
def factorial(n):
out = 1
while n >= 1:
out = out * n
n = n - 1
return out
print type(factorial(20))
print type(factorial(21))
produces
<type ’int’>
<type ’long’>
Very useful!
P. E. Farrell (Oxford)
Python I
May 6, 2015
7 / 21
Booleans and conditionals
Booleans behave as you expect:
>>> not False
True
>>> True and False
False
>>> True or False
True
Booleans can be used for conditionals:
>>> x = 5
>>> if 4 < x < 6:
...
print "x \in [4, 6]"
...
x \in [4, 6]
P. E. Farrell (Oxford)
Python I
May 6, 2015
8 / 21
Strings and slicing
You can make literal strings in several ways:
>>>
>>>
>>>
...
...
...
a = "This wouldn’t work with ’"
b = ’"Arrest that man!", he cried.’
c = """
A large block
of text
"""
Strings can be indexed and subscripted:
>>> b[0]
’"’
>>> b[1:7]
’Arrest’
>>> b[-6:]
’cried.’
P. E. Farrell (Oxford)
Python I
May 6, 2015
9 / 21
More strings
The Python str class is very powerful.
>>> dir(b)
... # too many methods to list
>>> b.swapcase()
’"aRREST THAT MAN!", HE CRIED.’
>>> b.replace("man", "donkey")
’"Arrest that donkey!", he cried.’
>>> print b + ’’’ "Why? He’s done nothing wrong."’’’
"Arrest that man!", he cried. "Why? He’s done nothing wrong."
>>> b.split(’!’)
[’"Arrest that man’, ’", he cried.’]
P. E. Farrell (Oxford)
Python I
May 6, 2015
10 / 21
String interpolation
Strings can be interpolated:
>>>
>>>
Our
>>>
>>>
Pi:
profit = 90210
print("Our annual profit was: EUR %s." % profit)
annual profit was: EUR 90210.
from math import pi
print("Pi: %1.5f" % pi)
3.14159
P. E. Farrell (Oxford)
Python I
May 6, 2015
11 / 21
Compound types: lists
Python has very powerful, fast native data structures. Here we’ll introduce
four: lists, tuples, dictionaries and sets.
A list contains, well, a list of objects of any kind:
>>> l = [1, 1, 2, 3, 5, 8]
>>> l += [’can’, ’be’, ’different’]
>>> l
[1, 1, 2, 3, 5, 8, ’can’, ’be’, ’different’]
>>> l[0]
1
>>> l[-1]
’different’
>>> range(4)
[0, 1, 2, 3]
>>> len(range(4))
4
P. E. Farrell (Oxford)
Python I
May 6, 2015
12 / 21
Compound types: lists
A list has several methods that modify it in-place:
>>> l.sort()
>>> l.reverse()
>>> l
[’different’, ’can’, ’be’, 8, 5, 3, 2, 1, 1]
>>> l[-1] = 10
Lists can be sliced and joined, too:
>>> l = [’c’, ’l’, ’o’, ’u’, ’d’, ’s’]
>>> l[1:5]
[’l’, ’o’, ’u’, ’d’]
>>> ’-’.join(l)
’c-l-o-u-d-s’
>>> ’’.join(l)
’clouds’
P. E. Farrell (Oxford)
Python I
May 6, 2015
13 / 21
Compound types: lists
The Python keyword for ∈ is in:
>>> l = range(5, 10)
>>> l
[5, 6, 7, 8, 9]
>>> 7 in l
True
>>> 10 in l
False
P. E. Farrell (Oxford)
Python I
May 6, 2015
14 / 21
List iteration
Lists
>>>
>>>
...
...
>>>
35
>>>
35
can be iterated over:
res = 0
for i in l:
res = res + i
res
sum(l)
P. E. Farrell (Oxford)
Python I
May 6, 2015
15 / 21
List comprehension
In mathematics, we write statements like:
V = x2 : x ∈ S
In Python, we can write analogous code: list comprehensions.
>>>
>>>
[0,
>>>
[0,
l = range(4)
[x**2 for x in l]
1, 4, 9]
[x**2 for x in l if x % 2 == 0]
4]
P. E. Farrell (Oxford)
Python I
May 6, 2015
16 / 21
Dictionaries
A dictionary is an unordered table that maps keys to values. The keys
have to be hashable.
>>> numbers = {"Carol": "2714", "Hannah": "3852", "Marie": "7268"}
>>> numbers["Carol"]
’2714’
>>> "Carol" in numbers
True
>>> numbers.keys()
[’Hannah’, ’Carol’, ’Marie’]
>>> numbers.values()
[’3852’, ’2714’, ’7268’]
>>> for name in numbers:
...
number = numbers[name]
...
print("%s -> %s" % (name, number))
...
Hannah -> 3852
Carol -> 2714
Marie -> 7268
P. E. Farrell (Oxford)
Python I
May 6, 2015
17 / 21
Dictionaries
The dictionary can natively map different types to different types.
>>> mydict = {’a’: 1, True: "hello"}
>>>
P. E. Farrell (Oxford)
Python I
May 6, 2015
18 / 21
Tuples
Lists are mutable: you can change them in-place. A tuple is an immutable
list. You can create and unpack tuples like so:
>>> t = (0, 1)
>>> t[0] = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: ’tuple’ object does not support item assignment
>>> (a, b) = t
>>> a
0
Because they can’t be changed after creation, tuples are used:
I to pass arguments into functions
I to return values from functions
I as keys in dictionaries.
P. E. Farrell (Oxford)
Python I
May 6, 2015
19 / 21
Sets
Our final in-built container type is the set.
>>> {1,
True
>>> {1,
True
>>> {1,
set([1,
2, 3, 3, 2} == {1, 2, 3}
2, 3} == {1, 3, 2}
3, 5}.union({2, 4})
2, 3, 4, 5])
P. E. Farrell (Oxford)
Python I
May 6, 2015
20 / 21
Python 01 Challenge!
Consider the recursion formula
un+3 = un+2 + ha
23
4
5
un+2 − un+1 + un ,
12
3
12
with n = 0, . . . , 1000, h = 1/1000, a = −1/2, u0 = exp (0),
u1 = exp (ha), u2 = exp (2ha).
(a) Create a list approx with the values of u, starting with the three
given values and completing it with the recursion formula.
(b) Create another list exact with values exp (anh).
(c) Create another list error with the difference between the two lists.
Hints:
from math import exp
for (e, a) in zip(exact, approx):
...
P. E. Farrell (Oxford)
Python I
May 6, 2015
21 / 21
Introduction to Python: functions
Patrick Farrell
MMSC: Python in Scientific Computing
May 6, 2015
P. E. Farrell (Oxford)
Python II
May 6, 2015
1 / 15
Functions
Functions are a central idea in mathematics.
def subtract(x1, x2):
return x1 - x2
Note that functions, like all other blocks, must be indented.
By default, arguments are assigned to parameters by order:
subtract(5.0, 4.3) # returns 0.7
We can also specify arguments by keyword:
subtract(x2=4.3, x1=5.0) # returns 0.7
P. E. Farrell (Oxford)
Python II
May 6, 2015
2 / 15
Input to functions
Default values for parameters can be specified:
def slicer(seq, start=None, stop=None, step=None):
return seq[start:stop:step]
seq = [str(i) for i in range(5)]
print(slicer(seq)) # [’0’, ’1’, ’2’, ’3’, ’4’]
print(slicer(seq, start=2)) # [’2’, ’3’, ’4’]
print(slicer(seq, start=2, stop=4)) # [’2’, ’3’]
You can also mix positional and keyword arguments:
slicer(seq, 4, step=-1, stop=1) # [’4’, ’3’, ’2’]
We can also pass arguments in a dictionary:
d = {’start’: 4, ’stop’: 1, ’step’: -1}
slicer(seq, **d) # [’4’, ’3’, ’2’]
P. E. Farrell (Oxford)
Python II
May 6, 2015
3 / 15
Input to functions
Functions can take a variable number of arguments, too:
def printer(*args, **kwargs):
print("args: %s" % (args,))
print("kwargs: %s" % kwargs)
printer(’hello’, ’world’, argument=4, another=’black’)
This prints
args: (’hello’, ’world’)
kwargs: {’argument’: 4, ’another’: ’black’}
P. E. Farrell (Oxford)
Python II
May 6, 2015
4 / 15
Output from functions
A function always returns one single object.
By default, functions return None:
from math import pi
def circle_area(radius):
area = pi * radius**2 # oops! forgot to return
area = circle_area(1.0) # returns None
If you want to return more than one value, construct a tuple:
from math import sqrt, atan2
def complex_to_polar(z):
r = sqrt(z.real**2 + z.imag**2)
phi = atan2(z.imag, z.real)
return (r, phi) # the tuple is one object
P. E. Farrell (Oxford)
Python II
May 6, 2015
5 / 15
Functions modifying inputs
Functions can modify mutable arguments (can be dangerous!).
def try_to_modify(a, b, c):
a = 23
b.append(’gotcha’)
c = [99, ’problems’]
a = 54
b = [’hello’]
c = [’world’]
try_to_modify(a, b, c)
a == 54 # True
b == [’hello’] # False
c == [’world’] # True
Be careful!
P. E. Farrell (Oxford)
Python II
May 6, 2015
6 / 15
Documenting functions
Functions carry their own documentation:
from math import pi
def circle_stats(radius):
"""This function computes the perimeter and area of a circle.
Usage:
(perimeter, area) = circle_stats(radius)
"""
return (2*pi*radius, pi*radius**2)
Python functions are objects like any other, and we can interrogate them
for their documentation:
print circle_stats.__doc__
help(circle_stats)
P. E. Farrell (Oxford)
Python II
May 6, 2015
7 / 15
Functions are objects
Functions are objects and can be passed in to other functions.
def deprecated(fun):
def wrapped_fun(*args, **kwargs):
print("Warning: function %s is deprecated." % fun)
return fun(*args, **kwargs)
return wrapped_fun
def myfun(x):
return x + 1
myfun = deprecated(myfun)
myfun(2)
P. E. Farrell (Oxford)
Python II
May 6, 2015
8 / 15
Functions are objects
This usage of functions modifying functions has a shorthand: decorators.
def deprecated(fun):
def wrapped_fun(*args, **kwargs):
print("Warning: function %s is deprecated." % fun)
fun(*args, **kwargs)
return wrapped_fun
@deprecated # equivalent to myfun = deprecated(myfun)
def myfun(x):
return x + 1
myfun(2)
P. E. Farrell (Oxford)
Python II
May 6, 2015
9 / 15
Recursive functions
Functions can call themselves:
def chebyshev(n, x):
"""This function computes the Chebyshev polynomial of
degree n at a point x with the recursion formula
T_n(x) = 2*x*T_{n-1}(x) - T_{n-2}(x)"""
print("chebyshev(%s, %s) called" % (n, x))
if n == 0:
return 1.0
elif n == 1:
return x
else:
return 2.0 * x * chebyshev(n - 1, x) - chebyshev(n - 2, x)
Recursive definitions are mathematically elegant but can be
computationally wasteful.
P. E. Farrell (Oxford)
Python II
May 6, 2015
10 / 15
Functions are objects
Let’s make it much faster with a decorator!
def memoise(fun):
cache = {}
def memoised_fun(*args, **kwargs):
kitems = tuple(kwargs.items())
key = (args, kitems)
if key not in cache:
cache[key] = fun(*args, **kwargs)
return cache[key]
return memoised_fun
@memoise
def chebyshev(n, x):
...
Introspection and dynamism are very very powerful.
P. E. Farrell (Oxford)
Python II
May 6, 2015
11 / 15
Useful decorators
Two extremely useful decorators: line_profiler and memory_profiler.
from line_profiler import profile
def sleep():
seconds = random.randint(0, 5); time.sleep(seconds)
@profile
def test():
sleep()
sleep()
sleep()
Line #
Hits
Time Per Hit
% Time Line Contents
==============================================================
8
@profile
9
def test():
10
1
3999416 3999416.0
36.4
sleep()
11
1
4999982 4999982.0
45.5
sleep()
12
1
1999990 1999990.0
18.2
sleep()
P. E. Farrell (Oxford)
Python II
May 6, 2015
12 / 15
Useful decorators
from memory_profiler import profile
@profile
def my_func():
a = [1] * (10 ** 6)
b = [2] * (2 * 10 ** 7)
del b
return a
Line #
Mem usage Increment
Line Contents
==============================================
3
@profile
4
5.97 MB
0.00 MB
def my_func():
5
13.61 MB
7.64 MB
a = [1] * (10 ** 6)
6
166.20 MB 152.59 MB
b = [2] * (2 * 10 ** 7)
7
13.61 MB -152.59 MB
del b
8
13.61 MB
0.00 MB
return a
P. E. Farrell (Oxford)
Python II
May 6, 2015
13 / 15
Anonymous functions
Functions can be defined anonymously (like matlab @):
def square(x):
return x*x
# Equivalently
square = lambda x: x*x
The lambda refers to λ-calculus.
P. E. Farrell (Oxford)
Python II
May 6, 2015
14 / 15
Python 02 Challenge!
(a) The previous challenge implemented the 3rd-order Adams-Bashforth
algorithm on the ODE
du
= au, u(0) = 1.0, u : [0, 1] 7→ R
dt
Write a function ab3(f, u0, t0, tT, N) that solves an ODE
du
= f (u), u(0) = u0 , u : [t0 , tT ] 7→ R
dt
using the 3rd-order Adams-Bashforth discretisation. (u0 is a tuple
with the first 3 values.)
(b) The greatest common divisor of two integers can be computed using
Euclid’s algorithm:
(
a,
if b = 0
gcd(a, b) =
gcd(b, a mod b), otherwise
Implement a function gcd(a, b).
P. E. Farrell (Oxford)
Python II
May 6, 2015
15 / 15
Introduction to Python: modules, namespaces and
plotting
Patrick Farrell
MMSC: Python in Scientific Computing
May 6, 2015
P. E. Farrell (Oxford)
Python III
May 6, 2015
1 / 15
Modules
Python code is organised into modules. Python comes with many powerful
modules by default.
We can import a module with the import statement:
import math
print(math.sin(math.pi)) # 1.22e-16
The math module defines its own namespace: a collection of Python
objects (e.g. variables, functions, classes). Namespaces allow clean
separation of code with the same name:
import math
import scipy
print(math.sin(math.pi)) # only works on single numbers
print(scipy.sin([math.pi, 2*math.pi])) # works on arrays
P. E. Farrell (Oxford)
Python III
May 6, 2015
2 / 15
Namespaces
We can also import objects from another namespace into our current
namespace. For example:
from math import sin, pi
print(sin(pi))
or even
from math import *
print(sin(pi))
P. E. Farrell (Oxford)
Python III
May 6, 2015
3 / 15
More importing
We can import modules with different names to their installed name. For
example:
import math as m
print(m.sin(m.pi))
We can also import elements of a namespace with a different name:
from math import sin as s, pi as p
print(s(p))
P. E. Farrell (Oxford)
Python III
May 6, 2015
4 / 15
Namespaces
We can investigate all of the objects a namespace exports with dir:
import math
print(dir(math))
For non-built-in modules, you can also find out where its code lives:
import scipy
print(scipy.__file__)
print(scipy.__version__)
P. E. Farrell (Oxford)
Python III
May 6, 2015
5 / 15
Making modules
You can make your own modules by putting your code in a file. Use your
favourite text editor to create a file fib.py:
def fibonacci(u0, u1, N):
l = [u0, u1]
for i in range(N-1):
l.append(l[-1] + l[-2])
return l
In another file, main.py:
import fib
print(fib.fibonacci(1, 1, 10))
P. E. Farrell (Oxford)
Python III
May 6, 2015
6 / 15
Making modules
Each namespace has a __name__ attribute:
>>> import math
>>> math.__name__
’math’
If this module is the main script being executed as python myscript.py,
its name is set to __main__. This can be used to protect test code:
def fibonacci(u0, u1, N):
l = [u0, u1]
for i in range(N-1):
l.append(l[-1] + l[-2])
return l
if __name__ == "__main__":
# won’t get executed on ’import fib’
f10 = fibonacci(1, 1, 10)
assert f10[-1] == 89
P. E. Farrell (Oxford)
Python III
May 6, 2015
7 / 15
Plotting
In the rest of this lecture we’ll look at one important module, matplotlib.
from scipy import *
from matplotlib.pyplot import *
x = linspace(-2*pi, 2*pi, 200)
plot(x, sin(x))
samples = x[::4]
plot(samples, sin(samples), ’bo’, markersize=10)
title("sin(x)")
grid()
savefig("sin.pdf")
P. E. Farrell (Oxford)
Python III
May 6, 2015
8 / 15
Plotting
sin(x)
1.0
0.5
0.0
0.5
1.0 8
P. E. Farrell (Oxford)
6
4
2
0
Python III
2
4
6
8
May 6, 2015
9 / 15
Plotting
from scipy import *
from matplotlib.pyplot import *
x = range(5)
y = [1, 2, 1, 3, 5]
p2 = polyfit(x, y, 2)
p4 = polyfit(x, y, 4)
xx = linspace(-1, 5, 200)
plot(xx, polyval(p2, xx), label="degree 2")
plot(xx, polyval(p4, xx), label="degree 4")
plot(x, y, ’b*’, markersize=10)
axis([-1, 5, 0,6])
legend(loc=’upper left’)
title(’Polynomial fitting’)
P. E. Farrell (Oxford)
Python III
May 6, 2015
10 / 15
Plotting
6
5
Polynomial fitting
degree 2
degree 4
4
3
2
1
01
P. E. Farrell (Oxford)
0
1
2
Python III
3
4
5
May 6, 2015
11 / 15
Plotting
from scipy import *
from matplotlib.pyplot import *
def mandelbrot(h, w, maxit=20):
’’’Returns image of Mandelbrot fractal of size (h, w)’’’
x = linspace(-2, 0.8, w)
y = linspace(-1.4, 1.4, h)
X, Y= meshgrid(x, y)
c = X + Y*1j
z = c
divtime = maxit + zeros(z.shape, dtype=int)
for iteration in xrange(maxit):
z = z**2 + c
diverge = z*conj(z) > 2**2
div_now = diverge & (divtime == maxit)
divtime[div_now] = iteration
z[diverge] = 2
return divtime
P. E. Farrell (Oxford)
Python III
May 6, 2015
12 / 15
Plotting
P. E. Farrell (Oxford)
Python III
May 6, 2015
13 / 15
Plotting
from mpl_toolkits.basemap import Basemap
from matplotlib.pyplot import *
m = Basemap(projection=’merc’, llcrnrlat=-10, urcrnrlat=80,
llcrnrlon=-140, urcrnrlon=100, lat_ts=40,
resolution=’h’, area_thresh=10000)
m.drawcoastlines()
m.drawcountries()
m.fillcontinents()
(ny_lat, ny_lon) = (40.6397, -73.7789)
(lon_lat, lon_lon) = (51.4775, 0.4614)
m.drawgreatcircle(lon_lon, lon_lat, ny_lon, ny_lat, linewidth=3)
savefig("greatcircle.pdf")
P. E. Farrell (Oxford)
Python III
May 6, 2015
14 / 15
Plotting
P. E. Farrell (Oxford)
Python III
May 6, 2015
15 / 15
Introduction to Python: iteration
Patrick Farrell
MMSC: Python in Scientific Computing
May 6, 2015
P. E. Farrell (Oxford)
Python IV
May 6, 2015
1 / 17
Iteration
Iteration is what makes computers useful. Your course is largely about
designing iterations that converge to some desired quantity, after all.
As we’ve seen, the simplest iteration in Python is looping over elements of
a list:
for s in [’a’, ’b’, ’c’]:
print(s), # prints ’a b c’
It’s also possible to iterate on a Boolean conditional:
while f(c) > tol:
# compute a better guess for the root c
return c
P. E. Farrell (Oxford)
Python IV
May 6, 2015
2 / 17
break, continue and pass
Sometimes we need to exit loops early. For example, interacting with users
could look like:
while True:
s = raw_input("Enter a string (quit to quit): ")
if s == "quit":
break
print("String length: %s" % len(s))
P. E. Farrell (Oxford)
Python IV
May 6, 2015
3 / 17
break, continue and pass
Sometimes we want to skip one particular iteration of a loop, but not exit
from the entire iteration:
for i in range(100):
if isprime(i):
continue
# Do something with composite number i
P. E. Farrell (Oxford)
Python IV
May 6, 2015
4 / 17
break, continue and pass
Sometimes we want to do nothing, but Python expects some valid code.
This is what the pass statement is for:
while True:
pass # will loop forever doing nothing
P. E. Farrell (Oxford)
Python IV
May 6, 2015
5 / 17
Iterable objects
for loops aren’t just for iterating over lists; we can iterate over any
iterable object:
for val in tuple:
...
for key in dict:
...
P. E. Farrell (Oxford)
Python IV
May 6, 2015
6 / 17
Iterable objects
Another example of an iterable object: files.
mydata = open("mydata.txt", "r")
for line in mydata:
print(line.split())
We’ll see more about I/O later.
P. E. Farrell (Oxford)
Python IV
May 6, 2015
7 / 17
Generators
In particular, we can iterate over an object that creates each value of the
iteration on-the-fly. Such an object is called a generator:
for i in range(1000000000):
# stores the entire list 0 to
# 1000000000 in memory all at ance
for i in xrange(1000000000):
# yields each item one at a time,
# O(1) memory usage
P. E. Farrell (Oxford)
Python IV
May 6, 2015
8 / 17
Generators
You can define your own generators with the yield keyword.
def fibonacci(u0, u1):
yield u0
yield u1
while True:
(u0, u1) = (u1, u0 + u1)
yield u1
for fib in fibonacci(1, 1):
# do something with Fibonacci numbers,
# will loop forever unless you break
This pattern is extremely useful for numerical computing: it separates
generating computed values, using them, and terminating.
P. E. Farrell (Oxford)
Python IV
May 6, 2015
9 / 17
Generator comprehensions
Another way to define a generator: like a list comprehension, but with
(...) instead of [..].
sum((x*x for x in xrange(10)))
In this special case (where the generator is the only argument), we can
drop the brackets:
sum(x*x for x in xrange(10))
P. E. Farrell (Oxford)
Python IV
May 6, 2015
10 / 17
Generators
Here are some useful tools.
enumerate takes in an iterator and produces a new iterator that yields
pairs (index, element):
A = [’a’, ’b’, ’c’]
for (index, x) in enumerate(A):
print(iteration, x)
# result: (0, ’a’) (1, ’b’) (2, ’c’)
P. E. Farrell (Oxford)
Python IV
May 6, 2015
11 / 17
Generators
reversed takes in a finite iterable and goes through this list backwards.
A = [’a’, ’b’, ’c’]
for x in reversed(A):
print(x)
# result: c b a
P. E. Farrell (Oxford)
Python IV
May 6, 2015
12 / 17
Generators
sorted takes in a finite iterable and returns an iterator that yields the
elements in order.
A = [’c’, ’b’, ’a’]
for x in sorted(A):
print(x)
# result: a b c
P. E. Farrell (Oxford)
Python IV
May 6, 2015
13 / 17
Generators
next takes in an iterator and returns the next value of the sequence.
from itertools import count
counter = count()
i = next(counter)
print(i) # 0
i = next(counter)
print(i) # 1
P. E. Farrell (Oxford)
Python IV
May 6, 2015
14 / 17
Generators
itertools.islice truncates an iterator.
from itertools import count, islice
for x in islice(count(), 10):
# same effect as xrange(10)
P. E. Farrell (Oxford)
Python IV
May 6, 2015
15 / 17
Python 04 Challenge!
Aitken’s ∆2 –method accelerates the convergence of sequences by
transforming a slowly-converging sequence into a faster-converging one
with the same limit. The formula is
ŝi = si −
(si+1 − si )2
.
si+2 − 2si+1 + si
Write a generator that takes in a generator for a sequence and yields the
Aitken acceleration of that sequence. As example input, use the sequence
sN
N
X
(−1)n
=
,
2n + 1
n=0
which converges to π/4 (slowly!).
Hint: value = next(sequence).
P. E. Farrell (Oxford)
Python IV
May 6, 2015
16 / 17
Python 04 MMSC Challenge!
MMSC students have to work harder (as they get credit).
Project Euler is a series of mathematical and programming puzzles to
teach programming and number theory. Any programming language can
be used, but the code must run in under 10 seconds.
Problem 48 reads: the series 11 + 22 + · · · + 1010 = 10405071317. Find
the last 10 digits of the series 11 + 22 + · · · + 10001000 .
Compute the answer in one line of Python that uses O(1) memory.
(Without googling.)
Email your one-liner and computed answer to
[email protected].
P. E. Farrell (Oxford)
Python IV
May 6, 2015
17 / 17
Introduction to Python: object orientation
Patrick Farrell
MMSC: Python in Scientific Computing
May 6, 2015
P. E. Farrell (Oxford)
Python V
May 6, 2015
1 / 16
Object orientation
Object orientation is a fundamental way to think about programming that
is often very useful for mathematical software.
P. E. Farrell (Oxford)
Python V
May 6, 2015
2 / 16
Object orientation
In mathematics, when we write sin, we refer to an object that we can
manipulate in various ways. For example, we can
I evaluate sin at a point x, returning a real number
I compute its derivative, returning another object cos
I compute the coefficients of its Taylor polynomial
These methods are shared between all sufficiently smooth functions.
Objects that share the same methods are grouped into classes.
P. E. Farrell (Oxford)
Python V
May 6, 2015
3 / 16
Object orientation
Given a class, we can instantiate it. sin is an instance of the class of
smooth functions. One of its attributes is its name. Another might be its
domain.
Consider a polynomial p(x). This is just like the sin function — every
function method also applies to p. But we can define other, special
methods for p that we cannot for sin: for example,
I return p’s coefficients, a list of real numbers
I factorise p, returning a list of polynomial factors
These methods define a class of polynomials, which inherit the methods of
the class of smooth functions, and add new ones on top.
P. E. Farrell (Oxford)
Python V
May 6, 2015
4 / 16
Object orientation
In mathematics, we often use the same symbol for different things. For
example, 5 + 4 and sin + cos have different meanings. But by using the
same symbol we express similarities of the mathematical operations.
So far we have introduced the concepts:
I classes
I instantiation
I inheritance
I methods
I attributes
I operator overloading
Next we’ll see how these work in Python.
P. E. Farrell (Oxford)
Python V
May 6, 2015
5 / 16
Defining classes
In Python, the class statement defines a new type:
class RationalNumber(object):
pass
Although this class doesn’t do much yet, we can instantiate it and
interrogate the objects created:
>>> a = RationalNumber()
>>> type(a)
<class __main__.RationalNumber>
>>> isinstance(a, RationalNumber)
True
P. E. Farrell (Oxford)
Python V
May 6, 2015
6 / 16
Now we provide our example class with some attributes.
To start with, let’s define the __init__ method used for initialising the
class:
class RationalNumber(object):
def __init__(self, numerator, denominator):
self.numerator = numerator
self.denominator = denominator
Let’s
>>>
>>>
5
>>>
15
see how this is used:
a = RationalNumber(5, 15)
a.numerator
a.denominator
The constructor sets two attributes.
P. E. Farrell (Oxford)
Python V
May 6, 2015
7 / 16
We can define other methods to do useful work with the objects.
class RationalNumber(object):
...
def add(self, other):
(p1, q1) = (self.numerator, self.denominator)
if isinstance(other, int):
(p2, q2) = (other, 1)
else:
(p2, q2) = (other.numerator, other.denominator)
return RationalNumber(p1*q2 + p2*q1, q1*q2)
Using this looks like:
>>> p = RationalNumber(1, 2)
>>> q = RationalNumber(1, 3)
>>> r = q.add(p)
>>> print("%s/%s" % (r.numerator, r.denominator))
5/6
P. E. Farrell (Oxford)
Python V
May 6, 2015
8 / 16
It would be much nicer if we could just write q + p instead. To define the
plus sign for RationalNumber, we can define the __add__ method. (Just
rename the add method). This means that we can write:
>>> p = RationalNumber(1, 2)
>>> q = RationalNumber(1, 3)
>>> r = q + p # alias for q.__add__(p)
We have overloaded the addition operator. In fact, that’s how addition is
defined for other types:
>>> hasattr(float, ’__add__’)
True
>>> hasattr(list, ’__add__’)
True
>>> hasattr(dict, ’__add__’)
False
P. E. Farrell (Oxford)
Python V
May 6, 2015
9 / 16
The __init__ and __add__ methods are so-called special methods that
mean something to the Python object system. Here we’ll define another:
class RationalNumber(object):
...
def __float__(self):
return float(self.numerator) / float(self.denominator)
This is used by Python’s float command:
>>> a = RationalNumber(5, 15)
>>> float(a)
0.3333333333333333
P. E. Farrell (Oxford)
Python V
May 6, 2015
10 / 16
Special methods
Here’s a flavour of some of the special methods (google ’special method
names’ for the full list):
I __str__: return a string representation of the object
I __cmp__: called for obj < other, obj > other, etc
I __call__: called when the object is called as a function
I __mul__, __div__: obj * other, obj / other
I __pow__: implements obj**x
I __abs__: implements abs(obj)
I __contains__: implements x in obj
I __iter__: iterate over an object
I __getitem__, __setitem__: getting and setting obj[x]
P. E. Farrell (Oxford)
Python V
May 6, 2015
11 / 16
Subclassing
Consider one-step methods for solving an ODE. An explicit one-step
method construct the solution values ui by the recursion steps
ui+1 = ui + hφ(f, ui , ti , h)
This is an abstract description that characterises many algorithms: to
actually use it, we have to fill in the details for φ. Different concrete
algorithms make different choices:
I Explicit Euler: φ = f (ui , ti )
I Midpoint rule: φ = f (ui + h/2f (ui ), ti + h/2)
I Runge-Kutta 4: φ = 1/6(s1 + 2s2 + 2s3 + s4 )
We will model this in Python with an abstract base class that collects the
methods common to all one-step methods, and make subclasses to fill in
the parameters for the concrete method.
P. E. Farrell (Oxford)
Python V
May 6, 2015
12 / 16
from numpy import linspace
class OneStepMethod(object):
def __init__(self, f, x0, interval, N):
self.f = f
self.x0 = x0
self.interval = [t0, tT] = interval
self.grid = linspace(t0, tT, N)
self.h = (tT - t0) / N
def generate(self):
(ti, ui) = (self.grid[0], self.x0)
yield (ti, ui)
for t in self.grid[1:]:
ui = ui + self.h * self.step(self.f, ui, ti, self.h)
ti = t
yield (ti, ui)
def solve(self):
return list(self.generate())
def step(self, f, u, t, h):
raise NotImplementedError
P. E. Farrell (Oxford)
Python V
May 6, 2015
13 / 16
We can inherit from this type to specialise it:
class ExplicitEuler(OneStepMethod):
def step(self, f, u, t, h):
return f(u, t)
class MidPointRule(OneStepMethod):
def step(self, f, u, t, h):
return f(u + h/2.0 * f(u, t), t + h/2.0)
OneStepMethod is called the parent class. Any method or attribute that is
not overridden is inherited.
P. E. Farrell (Oxford)
Python V
May 6, 2015
14 / 16
When overriding, it is sometimes useful to access the attributes of the
parent class:
class NewmarkBeta(OneStepMethod):
def __init__(self, f, x0, interval, N, beta=0.25):
self.beta = beta
OneStepMethod.__init__(self, f, x0, interval, N)
Here, we override the constructor of the NewmarkBeta class, which in turn
must call the constructor of the subclass for the inherited methods to work.
P. E. Farrell (Oxford)
Python V
May 6, 2015
15 / 16
Python 05 Challenge!
Implement a Polynomial class with the following methods:
I Construction from coefficients, or from x–y values
I Addition
I Evaluation at a point
I Differentiation and integration (to give new polynomials)
I Roots
Test your code on examples with known answers (e.g. from Wolfram
Alpha).
Extra challenge: implement multiplication of two polynomials.
Does the code 5 + p work, where p is a Polynomial? Hint: look up the
difference between __add__ and __radd__.
P. E. Farrell (Oxford)
Python V
May 6, 2015
16 / 16
Day 1 Review Challenge!
Implement a recursive fibonacci(N) function that computes the N th
Fibonacci number. Include a print statement to trace the execution.
Once that is working, memoise the recursive function with a decorator.
Compare the number of executions before and after for fibonacci(25).
P. E. Farrell (Oxford)
Python VI
May 6, 2015
1 / 12
Introduction to Python: errors and debugging
Patrick Farrell
MMSC: Python in Scientific Computing
May 6, 2015
P. E. Farrell (Oxford)
Python VI
May 6, 2015
2 / 12
Exceptions
One error all programmers see is where code has incorrect syntax:
>>> for i in range(10)
File "<stdin>", line 1
for i in range(10)
^
SyntaxError: invalid syntax
This is an example of an exception being raised.
P. E. Farrell (Oxford)
Python VI
May 6, 2015
3 / 12
Exceptions
More examples of built-in exceptions:
>>> 1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
>>>
>>> [’a’, ’b’, ’c’][4]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>>
>>> float(1.0 + 1.0j)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can’t convert complex to float
P. E. Farrell (Oxford)
Python VI
May 6, 2015
4 / 12
Exceptions
Creating an error is called raising an exception. You can raise an exception
with the raise statement:
raise Exception("Something went wrong")
For example, we might
def factorial(n):
if n < 0:
raise ValueError("A nonnegative integer is expected")
...
Why do this over printing an error message?
I Print statements are easy to miss, if the message is buried in many
other messages.
I With a print statement, calling code won’t know something went
wrong, and handle it.
P. E. Farrell (Oxford)
Python VI
May 6, 2015
5 / 12
Exceptions
Dealing with exceptions is referred to a catching an exception. We use
three statements for this: try, except, finally.
try:
f = open("data.txt", "r")
data = f.readline()
value = float(data)
except IOError:
print("Caught an IOError! Maybe couldn’t open the file?")
except ValueError:
print("Caught a ValueError! Maybe couldn’t convert to float?")
finally:
f.close()
P. E. Farrell (Oxford)
Python VI
May 6, 2015
6 / 12
Exceptions
You can also define your own exceptions:
class ConvergenceError(Exception):
pass
# later on ...
if iters > max_iters:
raise ConvergenceError("Algorithm did not converge")
P. E. Farrell (Oxford)
Python VI
May 6, 2015
7 / 12
Context managers
There is a very useful construct in Python for simplifying exception
handling when working with “contexts” like files or databases: the with
statement.
with open("data.txt", "r") as f:
# process f
The open function returns a context manager that ensures that the file
handle is closed when the block terminates.
P. E. Farrell (Oxford)
Python VI
May 6, 2015
8 / 12
Context managers
Context managers are defined by two special methods: __enter__ and
__exit__. Here are some more examples, from threaded parallelism:
import threading
lock = threading.Lock()
with lock:
# do something with the protected resource
and from connecting to a database:
import sqlite
with sqlite.connect(db) as conn:
query = "INSERT INTO table VALUES (’hello’, ’world’)"
conn.execute(query)
P. E. Farrell (Oxford)
Python VI
May 6, 2015
9 / 12
Context managers
Numerically, one of the more useful is numpy’s errstate:
from numpy import errstate, sqrt
with errstate(invalid=’ignore’):
print(sqrt(-1)) # prints ’nan’
with errstate(invalid=’warn’):
print(sqrt(-1)) # prints ’nan’ and ’RuntimeWarning’
with errstate(invalid=’raise’):
print(sqrt(-1)) # raises FloatingPointError
P. E. Farrell (Oxford)
Python VI
May 6, 2015
10 / 12
Debugging
When an Exception is raised, you see the call stack. The call stack
contains the trace of all the functions that called the code where the
exception was raised. Consider the fle callstack.py:
def f():
g()
def g():
h()
def h():
1/0
f()
This yields the stack trace
Traceback (most recent call last):
File "callstack.py", line 10, in <module>
f()
File "callstack.py", line 2, in f
g()
File "callstack.py", line 5, in g
h()
File "callstack.py", line 8, in h
1/0
ZeroDivisionError: integer division or modulo by zero
P. E. Farrell (Oxford)
Python VI
May 6, 2015
11 / 12
Debugging
Start the debugger by calling pdb.set_trace at the appropriate point in
your code:
import pdb
from math import sqrt, atan2
def complex_to_polar(z):
pdb.set_trace()
r = sqrt(z.real**2 + z.imag**2)
phi = atan2(z.imag, z.real)
return (r, phi)
complex_to_polar(3 + 5j)
This starts a debugger at the specified line:
[pef@aislinn:/tmp]$ python bugs.py
> /tmp/showpdb.py(6)complex_to_polar()
-> r = sqrt(z.real**2 + z.imag**2)
(Pdb)
P. E. Farrell (Oxford)
Python VI
May 6, 2015
12 / 12
Introduction to Python: NumPy
Patrick Farrell
MMSC: Python in Scientific Computing
May 7, 2015
P. E. Farrell (Oxford)
Python VII
May 7, 2015
1 / 15
NumPy
Much of the scientific stack for Python is built on top of numpy. numpy
provides highly optimised implementations of the fundamental datatypes
for linear algebra: vectors, matrices, and their higher-dimensional
analogues.
>>> import numpy as np
>>> a = np.array([0, 1, 2, 3])
>>> a
array([0, 1, 2, 3])
Key differences from lists:
I Fixed data type
I Fixed size =⇒ vectorisation
I Much, much faster
P. E. Farrell (Oxford)
Python VII
May 7, 2015
2 / 15
NumPy
>>> b = np.arange(1, 4, 0.5) # start, stop, step
>>> b
array([ 1. , 1.5, 2. , 2.5, 3. , 3.5])
>>> c = np.linspace(0, 1, 6)
# start, step, num-points
>>> c
array([ 0. , 0.2, 0.4, 0.6, 0.8, 1. ])
>>> a = np.ones((3, 3)) # reminder: (3, 3) is a tuple
>>> a
array([[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.]])
>>> b = np.zeros((2, 2))
>>> b
array([[ 0., 0.],
[ 0., 0.]])
>>> d = np.diag(np.array([1, 2, 3, 4]))
>>> d
array([[1, 0, 0, 0],
[0, 2, 0, 0],
[0, 0, 3, 0],
[0, 0, 0, 4]])
P. E. Farrell (Oxford)
Python VII
May 7, 2015
3 / 15
Datatypes
Numpy arrays have a datatype. With the array constructor, numpy creates
arrays with the smallest datatype that can contain the given data.
>>> a = np.array([1, 2, 3])
>>> a.dtype
dtype(’int64’)
>>> a[0] = 0.5 # danger! will be cast to int64
>>> a
array([0, 2, 3])
>>> b = np.array([1., 2., 3.])
>>> b.dtype
dtype(’float64’)
P. E. Farrell (Oxford)
Python VII
May 7, 2015
4 / 15
Slicing
Numpy arrays can be sliced in all sorts of powerful ways:
>>> M = np.random.randn(10, 10)
>>> M[2:4, 5:8]
array([[ 0.57563161, -1.81466408,
[-2.40016935, -0.42627965,
>>> M[2:4, 5:8] = 0.0
0.98266811],
0.80222344]])
These simple slices create views into the dataset.
P. E. Farrell (Oxford)
Python VII
May 7, 2015
5 / 15
Example: Sieve of Eratosthenes
import numpy as np
def primes(N):
is_prime = np.ones(N, dtype=bool)
is_prime[:2] = False # 0, 1 not prime
N_sqrt = int(np.sqrt(N))
for j in range(2, N_sqrt):
is_prime[2*j::j] = False
return np.nonzero(is_prime)[0]
if __name__ == "__main__":
import sys
N = int(sys.argv[1]) # read in from command-line
print(primes(N))
P. E. Farrell (Oxford)
Python VII
May 7, 2015
6 / 15
Fancy indexing
Numpy arrays can also be indexed with Boolean or integer arrays (masks).
These create copies, not views.
>>> a = np.random.random_integers(0, 20, 15)
>>> a
array([10, 3, 8, 0, 19, 10, 11, 9, 10, 6, 0, 20, 12, 7, 14])
>>> (a % 3 == 0)
array([False, True, False, True, False, False, False, True, False,
True, True, False, True, False, False], dtype=bool)
>>> mask = (a % 3 == 0)
>>> extract_from_a = a[mask] # or, a[a%3==0]
>>> extract_from_a
# extract a sub-array with the mask
array([ 3, 0, 9, 6, 0, 12])
We actually saw this earlier in plotting the Mandelbrot fractal.
P. E. Farrell (Oxford)
Python VII
May 7, 2015
7 / 15
Operations on arrays
All arithmetic operates elementwise.
>>> a = np.array([1, 2, 3, 4])
>>> a + 1
array([2, 3, 4, 5])
>>> 2**a
array([ 2, 4, 8, 16])
>>> b = np.ones(4) + 1
>>> a - b
array([-1., 0., 1., 2.])
>>> a * b
array([ 2., 4., 6., 8.])
These operations are vectorised: much faster than if implemented in
Python.
P. E. Farrell (Oxford)
Python VII
May 7, 2015
8 / 15
More examples
>>> a = np.array([1, 1, 0, 0], dtype=bool)
>>> b = np.array([1, 0, 1, 0], dtype=bool)
>>> a == b
array([ True, False, False, True], dtype=bool)
>>> a > b
array([False, True, False, False], dtype=bool)
>>> a & b
array([ True, False, False, False], dtype=bool)
>>> a | b
array([ True, True, True, False], dtype=bool)
>>> np.sin(a) # elementwise
array([ 0.84130859, 0.84130859, 0.
, 0.
P. E. Farrell (Oxford)
Python VII
May 7, 2015
])
9 / 15
Reductions
>>> x = np.array([[1, 1], [2, 2]])
>>> x
array([[1, 1],
[2, 2]])
>>> x.sum(axis=0)
# columns (first dimension)
array([3, 3])
>>> x[:, 0].sum(), x[:, 1].sum()
(3, 3)
>>> x.sum(axis=1)
# rows (second dimension)
array([2, 4])
>>> x[0, :].sum(), x[1, :].sum()
(2, 4)
P. E. Farrell (Oxford)
Python VII
May 7, 2015
10 / 15
Broadcasting
.
np arange(3)+5
+
0 1 2
.
5 5 5
=
5 6 7
=
1 2 3
1 2 3
1 2 3
, 3))+np.arange(3)
np ones((3
1 1 1
1 1 1
1 1 1
.
+
.
0 1 2
0 1 2
0 1 2
, 1))+np.arange(3)
np arange(3) reshape((3
0 0 0
1 1 1
2 2 2
P. E. Farrell (Oxford)
+
0 1 2
0 1 2
0 1 2
Python VII
=
0 1 2
1 2 3
2 3 4
May 7, 2015
11 / 15
Shape manipulation
>>> a = np.array([[1, 2, 3], [4, 5, 6]])
>>> a.ravel()
array([1, 2, 3, 4, 5, 6])
>>> a.T
array([[1, 4],
[2, 5],
[3, 6]])
>>> a.T.ravel()
array([1, 4, 2, 5, 3, 6])
P. E. Farrell (Oxford)
Python VII
May 7, 2015
12 / 15
Shape manipulation
>>> a.shape
(2, 3)
>>> b = a.ravel()
>>> b = b.reshape((2, 3))
>>> b
array([[1, 2, 3],
[4, 5, 6]])
>>> b[0, 0] = 99
>>> a
array([[99, 2, 3],
[ 4, 5, 6]])
P. E. Farrell (Oxford)
Python VII
May 7, 2015
13 / 15
Python 07 Challenge!
Conway’s Game of Life is a cellular automaton that creates beautiful
patterns. Given an N × N grid of cells, each cell is either dead (0) or alive
(1). Every cell interacts with its eight neighbours. At each step in time,
the following transitions occur:
I Any live cell with fewer than two neighbours dies (underpopulation).
I Any live cell with two or three neighbours lives on (survival).
I Any live cell with more than three neighbours dies (overcrowding).
I Any dead cell with exactly three live neighbours becomes alive
(reproduction).
Represent the N × N grid with an (N + 2) × (N + 2) numpy matrix bordered by
zeros. Implement:
I a neighbours function that counts the live neighbours of each cell.
I an iterate function that applies the rules.
I call your code on the glider pattern [[0, 0, 1], [1, 0, 1], [0, 1, 1]].
P. E. Farrell (Oxford)
Python VII
May 7, 2015
14 / 15
P. E. Farrell (Oxford)
Python VII
May 7, 2015
15 / 15
Introduction to Python: SciPy
Patrick Farrell
MMSC: Python in Scientific Computing
May 6, 2015
P. E. Farrell (Oxford)
Python VIII
May 6, 2015
1 / 10
SciPy
NumPy provides the core dense multidimensional array class for Python.
SciPy provides basically everything else:
scipy.linalg
scipy.integrate
scipy.fftpack
scipy.io
scipy.sparse
scipy.interpolate
scipy.optimize
scipy.stats
Linear algebra
Quadrature
Fourier transforms
I/O
Sparse matrices
Interpolation
Optimisation
Statistics
Let’s take a closer look at some of them.
P. E. Farrell (Oxford)
Python VIII
May 6, 2015
2 / 10
Interoperability with MATLAB
>>> from scipy import io as spio
>>> a = np.ones((3, 3))
>>> spio.savemat(’file.mat’, {’a’: a})
>>> data = spio.loadmat(’file.mat’)
>>> data[’a’]
array([[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.]])
Note: for .mat 7.3 format, use HDF5 via h5py.
P. E. Farrell (Oxford)
Python VIII
May 6, 2015
3 / 10
Dense linear algebra
Determinants:
>>> from scipy import linalg
>>> arr = np.array([[1, 2],
...
[3, 4]])
>>> linalg.det(arr)
-2.0
>>> arr = np.array([[3, 2],
...
[6, 4]])
>>> linalg.det(arr)
0.0
>>> linalg.det(np.ones((3, 4)))
Traceback (most recent call last):
...
ValueError: expected square matrix
P. E. Farrell (Oxford)
Python VIII
May 6, 2015
4 / 10
Dense linear algebra
Matrix inverses:
>>> arr = np.array([[1, 2],
...
[3, 4]])
>>> iarr = linalg.inv(arr)
>>> iarr
array([[-2. , 1. ],
[ 1.5, -0.5]])
>>> np.allclose(np.dot(arr, iarr), np.eye(2))
True
P. E. Farrell (Oxford)
Python VIII
May 6, 2015
5 / 10
Dense linear algebra
Singular value decompositions:
>>> H = linalg.hilbert(3)
>>> (U, L, VT) = linalg.svd(H)
>>> L
array([ 1.40831893, 0.12232707,
P. E. Farrell (Oxford)
Python VIII
0.00268734])
May 6, 2015
6 / 10
Dense linear algebra
Some more routines (see help(scipy.linalg) for the full list):
solve
norm
pinv
eig
qr
expm
P. E. Farrell (Oxford)
Solve with LU
Matrix and vector norms
Moore–Penrose pseudoinverse
Eigenvalues
QR factorisation
Matrix exponentiation
Python VIII
May 6, 2015
7 / 10
Sparse linear algebra
Almost all applications in scientific computing involve sparse matrices.
scipy.sparse implements several efficient sparse matrix data formats.
You should be aware of two:
I List of lists format ("lil"): efficient for construction and
modification
I Compressed sparse row format ("csr"): efficient for matvec and
sparse LU
P. E. Farrell (Oxford)
Python VIII
May 6, 2015
8 / 10
Example: 1D Laplacian with homogeneous Dirichlet BCs
import scipy.sparse as sp
import scipy.sparse.linalg as la
import numpy as np
N = 1000; h = 1.0 / (N+1)
K = sp.diags(diagonals=[2, -1, -1], offsets=[0, -1, 1],
shape=(N, N), format="lil")
rhs = 2 * np.ones(N) * h**2
K[+0,:] = 0.0;
K[-1,:] = 0.0;
rhs[+0] = 0.0;
K[+0, +0] = 1.0
K[-1, -1] = 1.0
rhs[-1]
= 0.0
K = K.tocsr()
u = la.spsolve(K, rhs)
P. E. Farrell (Oxford)
Python VIII
May 6, 2015
9 / 10
Python 08 Challenge!
Consider the one-dimensional Gray–Scott equations:
ut = u ∆u − uv 2 + F (1 − u)
vt = v ∆v + uv 2 − (c + F )v
with periodic boundary conditions on [−1, 1).
(a) Solve the zero-diffusion case with c = 0.065, F = 0.06 with forward
Euler, for various initial conditions. Plot the steady-state solutions.
(b) Take u = 6 × 10−5 , v = 2 × 10−5 . Use the initial condition
v(x) = χ(x) + 0.05η(x)
u(x) = 1 − v(x)
where η(x) ∼ N (0, 1) and
(
1 if |x − 0.1| ≤ 0.1
χ(x) =
0 otherwise
P. E. Farrell (Oxford)
Python VIII
May 6, 2015
10 / 10
Implementing finite element models in Python/FEniCS:
static linear PDEs
Patrick Farrell, Hans Petter Langtangen, Anders Logg, Marie Rognes,
André Massing
MMSC: Python in Scientific Computing
May 6, 2015
P. E. Farrell (Oxford)
FEniCS I
May 6, 2015
1 / 17
FEniCS is an automated programming environment for
differential equations
I C++/Python library
I Initiated 2003 in Chicago
I 1000–2000 monthly downloads
I Licensed under the GNU LGPL
http://fenicsproject.org/
Collaborators
Simula Research Laboratory, University of Cambridge, University of
Chicago, Texas Tech University, KTH Royal Institute of Technology,
Chalmers University of Technology, Imperial College London, University of
Oxford, . . .
P. E. Farrell (Oxford)
FEniCS I
May 6, 2015
2 / 17
Poisson: the Hello World of PDEs
Consider Poisson’s equation with Dirichlet boundary conditions:
−∆u = f
in Ω
u=g
on ∂Ω
Poisson’s equation arises in numerous applications:
I heat conduction, electrostatics, diffusion of substances, twisting of
elastic rods, inviscid fluid flow, water waves, magnetostatics, . . .
I as part of numerical splitting strategies for more complicated systems
of PDEs, e.g. the Navier–Stokes equations, bidomain equations,
Cahn-Hilliard
P. E. Farrell (Oxford)
FEniCS I
May 6, 2015
3 / 17
From PDE to variational problem
We multiply the PDE by a test function v and integrate over Ω:
Z
Z
− (∆u)v dx =
f v dx
Ω
Ω
Then integrate by parts and set v = 0 on the Dirichlet boundary:
Z
Z
Z
∂u
v ds
− (∆u)v dx =
∇u · ∇v dx −
Ω
Ω
∂Ω ∂n
|
{z
}
=0
In weak form, the equation is:
Z
Z
f v dx
∇u · ∇v dx =
Ω
P. E. Farrell (Oxford)
Ω
FEniCS I
May 6, 2015
4 / 17
A test problem
We construct a test problem for which we can easily check the answer. We
first define the exact solution by
u(x, y) = 1 + x2 + 2y 2
We insert this into Poisson’s equation:
f = −∆u = −∆(1 + x2 + 2y 2 ) = −(2 + 4) = −6
This technique is called the method of manufactured solutions (MMS).
P. E. Farrell (Oxford)
FEniCS I
May 6, 2015
5 / 17
Hello World in FEniCS: implementation
from dolfin import *
mesh = UnitSquareMesh(32, 32)
V = FunctionSpace(mesh, "Lagrange", 1)
u = Function(V)
v = TestFunction(V)
f = Constant(-6.0)
g = Expression("1 + x[0]*x[0] + 2*x[1]*x[1]")
bc = DirichletBC(V, g, DomainBoundary())
F = inner(grad(u), grad(v))*dx - f*v*dx
solve(F == 0, u, bc)
plot(u, interactive=True)
P. E. Farrell (Oxford)
FEniCS I
May 6, 2015
6 / 17
Step by step: the first line
The first line of a FEniCS program usually begins with
from dolfin import *
This imports key classes like UnitSquareMesh, FunctionSpace,
Function and so forth, from the FEniCS user interface (DOLFIN).
P. E. Farrell (Oxford)
FEniCS I
May 6, 2015
7 / 17
Step by step: creating a mesh
Next, we create a mesh of our domain Ω:
mesh = UnitSquareMesh(32, 32)
defines a (triangular) mesh with 32 elements along each edge.
Other useful classes for creating meshes include UnitIntervalMesh,
UnitCubeMesh, UnitCircleMesh, UnitSphereMesh, RectangleMesh
and BoxMesh.
Complex geometries should be built in dedicated mesh generation tools and
imported:
mesh = Mesh("complexmesh.xdmf")
Mesh generation is a huge subject in its own right, outside the scope of this
course.
P. E. Farrell (Oxford)
FEniCS I
May 6, 2015
8 / 17
Step by step: creating a function space
The following line creates a function space on Ω:
V = FunctionSpace(mesh, "Lagrange", 1)
The second argument specifies the type of element, while the third
argument is the degree of the basis functions on the element.
Other types of elements include "Discontinuous Lagrange",
"Brezzi-Douglas-Marini", "Raviart-Thomas",
"Crouzeix-Raviart", "Nedelec 1st kind H(curl)" and "Nedelec
2nd kind H(curl)". See help(FunctionSpace) for a list.
P. E. Farrell (Oxford)
FEniCS I
May 6, 2015
9 / 17
Step by step: defining expressions
Next, we define an expression for the boundary value:
g = Expression("1 + x[0]*x[0] + 2*x[1]*x[1]")
The formula must be written in C++ syntax.
Optionally, a polynomial degree may be specified
g = Expression("1 + x[0]*x[0] + 2*x[1]*x[1]", degree=2)
The Expression class is very flexible and can be used to create complex
user-defined expressions. For more information, try help(Expression).
P. E. Farrell (Oxford)
FEniCS I
May 6, 2015
10 / 17
Step by step: defining a boundary condition
The following code defines a Dirichlet boundary condition:
bc = DirichletBC(V, g, DomainBoundary())
This boundary condition states that a function in the function space
defined by V should be equal to g on the domain defined by
DomainBoundary().
Note that the above line does not yet apply the boundary condition to all
functions in the function space. (It gets enforced strongly during solve.)
P. E. Farrell (Oxford)
FEniCS I
May 6, 2015
11 / 17
Step by step: more about defining domains
For a Dirichlet boundary condition, a simple domain can be defined by a string
"on_boundary" # The entire boundary
Alternatively, domains can be defined by subclassing SubDomain
class Boundary(SubDomain):
def inside(self, x, on_boundary):
return on_boundary
You may want to experiment with the definition of the boundary:
"near(x[0], 0.0)" # x_0 = 0
"near(x[0], 0.0) || near(x[1], 1.0)"
There are many more possibilities, see
help(SubDomain)
help(DirichletBC)
P. E. Farrell (Oxford)
FEniCS I
May 6, 2015
12 / 17
Step by step: defining the right-hand side
The right-hand side f = −6 may be defined as follows:
f = Expression("-6")
or (more efficiently) as
f = Constant(-6.0)
Using a Constant means that the intermediate C++ code won’t be
regenerated when the value changes.
P. E. Farrell (Oxford)
FEniCS I
May 6, 2015
13 / 17
Step by step: defining variational problems
Variational problems are defined in terms of solution and test functions:
u = Function(V)
v = TestFunction(V)
We now have all the objects we need in order to specify the form:
F = inner(grad(u), grad(v))*dx - f*v*dx
Here dx is a type of class Measure that means ”integrate over the whole
volume”. There are other measures: for example, ds means ”integrate
over exterior facets”.
P. E. Farrell (Oxford)
FEniCS I
May 6, 2015
14 / 17
Step by step: solving variational problems
Once a variational problem has been defined, it may be solved by calling
the solve function:
solve(F == 0, u, bc)
Nice!
P. E. Farrell (Oxford)
FEniCS I
May 6, 2015
15 / 17
Step by step: post-processing
The solution and the mesh may be plotted by calling:
plot(u, interactive=True)
The interactive=True argument is necessary for the plot to remain on
the screen and allows the plots to be rotated, translated and zoomed.
For postprocessing in ParaView, store the solution in XDMF format:
file = File("poisson.xdmf")
file << u
P. E. Farrell (Oxford)
FEniCS I
May 6, 2015
16 / 17
FEniCS 01 Challenge!
I Run the code and look at the solution.
I Change the domain to [0, 1]3 . (Choose a new solution and fix the
source term/BC to suit.)
I Try using quadratic Lagrange elements.
I A crucial step in developing solvers is verifying their implementation.
I Wrap the 2D linear Poisson solver in a function that takes in the
mesh size N and returns the L2 and H 1 errors. (Hint:
help(errornorm)).
I Solve the problem for N=10, 20, 40 and store the errors in a
list.
I Verify that the approximation is converging at the expected rate.
I What is the scaling of the method (time in seconds vs number of
degrees of freedom)? How would you make an O(n) method?
P. E. Farrell (Oxford)
FEniCS I
May 6, 2015
17 / 17
Implementing finite element models in Python/FEniCS:
time-dependent linear PDEs
Patrick Farrell, Hans Petter Langtangen, Anders Logg, Marie Rognes
MMSC: Python in Scientific Computing
May 6, 2015
P. E. Farrell (Oxford)
FEniCS II
May 6, 2015
1 / 10
The heat equation
We will solve the simplest extension of the Poisson problem into the time
domain, the heat equation:
∂u
− ∆u = f in Ω for t > 0
∂t
u = g on ∂Ω for t > 0
u = u0 in Ω at t = 0
The solution u = u(x, t), the right-hand side f = f (x, t) and the boundary
value g = g(x, t) may vary in space (x = (x0 , x1 , ...)) and time (t). The
initial value u0 is a function of space only.
P. E. Farrell (Oxford)
FEniCS II
May 6, 2015
2 / 10
Time-discretization of the heat equation
We discretize in time using the backward Euler method:
∂u n
un − un−1
(t ) ≈
, u(tn ) ≈ un ,
∂t
∆t
Semi-discretization of the heat equation:
f n = f (tn )
un − un−1
− ∆un = f n
∆t
Algorithm
I Start with u0 and choose a timestep ∆t > 0.
I For n = 1, 2, . . ., solve for un :
un − ∆t∆un = un−1 + ∆tf n
P. E. Farrell (Oxford)
FEniCS II
May 6, 2015
3 / 10
Variational problem for the heat equation
Find un ∈ V n such that
a(un , v) = Ln (v)
for all v ∈ V̂ where
Z
uv + ∆t∇u · ∇v dx
a(u, v) =
Ln (v) =
ZΩ
un−1 v + ∆tf n v dx
Ω
Note that the bilinear form a(u, v) is constant in time while the linear form
Ln depends on time.
P. E. Farrell (Oxford)
FEniCS II
May 6, 2015
4 / 10
Test problem
As in the steady case, we construct a test problem for which we can easily
check the answer. We first define the exact solution by
u = 1 + x2 + αy 2 + βt
We insert this into the heat equation:
f = u̇ − ∆u = β − 2 − 2α
The initial condition is
u0 = 1 + x2 + αy 2
This technique is called the method of manufactured solutions.
P. E. Farrell (Oxford)
FEniCS II
May 6, 2015
5 / 10
Handling time-dependent expressions
We need to define a time-dependent expression for the boundary value:
alpha = 3
beta = 1.2
g = Expression("1 + x[0]*x[0] + alpha*x[1]*x[1] + beta*t",
alpha=alpha, beta=beta, t=0)
Updating parameter values:
g.t = t
P. E. Farrell (Oxford)
FEniCS II
May 6, 2015
6 / 10
Projection and interpolation
We need to project the initial value into Vh :
u0 = project(g, V)
We can also interpolate the initial value into Vh :
u0 = interpolate(g, V)
P. E. Farrell (Oxford)
FEniCS II
May 6, 2015
7 / 10
Implementation
from dolfin import *
# Mesh and function space
mesh = UnitSquareMesh(32, 32)
V = FunctionSpace(mesh, "Lagrange", 1)
# Time variables
dt = Constant(0.3); t = float(dt); T = 1.8
g_expr = "1 + x[0]*x[0] + alpha*x[1]*x[1] + beta*t"
g = Expression(g_expr, alpha=3.0, beta=1.2, t=0, degree=2)
# Previous and current solution
u0 = interpolate(g, V); u1 = Function(V)
# Variational problem at each time
u = TrialFunction(V)
v = TestFunction(V)
f = Constant(1.2 - 2. - 2*3.0)
a = u*v*dx + dt*inner(grad(u), grad(v))*dx
L = u0*v*dx + dt*f*v*dx
bc = DirichletBC(V, g, "on_boundary")
while t <= T:
g.t = t
solve(a == L, u1, bc)
u0.assign(u1)
t += float(dt)
plot(u1, interactive=True)
P. E. Farrell (Oxford)
FEniCS II
May 6, 2015
8 / 10
Remark on efficiency
The previous code will reassemble the mass and stiffness matrices every
timestep.
Where efficiency is a concern, you can call assemble to assemble matrices
outside of the time loop. This makes your code faster, but uglier too:
A = assemble(a) # A is a Matrix
b = assemble(L) # b is a Vector
solve(A, u.vector(), b)
P. E. Farrell (Oxford)
FEniCS II
May 6, 2015
9 / 10
FEniCS 02 Challenge!
I Run the code and look at the solution.
(Try moving the plot inside the timeloop.)
I Change the chosen solution such that f is time-dependent.
Make sure to update anything necessary in the time loop.
I Change the code to use forward Euler. What happens?
I Change the code to implement the θ-method, and compare the
results.
P. E. Farrell (Oxford)
FEniCS II
May 6, 2015
10 / 10
Implementing finite element models in Python/FEniCS:
steady nonlinear PDEs
Patrick Farrell, Hans Petter Langtangen, Marie Rognes, Garth Wells
MMSC: Python in Scientific Computing
May 6, 2015
P. E. Farrell (Oxford)
FEniCS III
May 6, 2015
1/7
Nonlinear problems
Consider a Poisson-type equation where the diffusivity depends on the
solution itself:
−∇ · (γ(u)∇u) = f
in Ω
u=g
on ∂Ω
where
γ(u) = (2 +
1
|∇u|2 )(p−2)/2
2
This particular choice of γ defines the p-Laplace equation.
P. E. Farrell (Oxford)
FEniCS III
May 6, 2015
2/7
Canonical nonlinear variational problem
The following canonical notation is used for (possibly) nonlinear problems:
Nonlinear variational problem
Find u ∈ V such that
F(u; v) = 0
for all v ∈ V̂ .
F is a rank-one form: test function, no trial function. When assembled at
a given u, F yields a vector.
Sooner or later your code always becomes nonlinear.
P. E. Farrell (Oxford)
FEniCS III
May 6, 2015
3/7
Variational formulation
Multiplying with a test function and integrating by parts yields
Z
Z
F(u; v) =
∇v · γ(u)∇u dx −
f v dx.
Ω
Ω
In UFL, this is written as
def gamma(u):
return (epsilon**2 + 0.5 * inner(grad(u), grad(u)))**((p-2)/2)
F = inner(grad(v), gamma(u) * grad(u))*dx - inner(f, v)*dx
P. E. Farrell (Oxford)
FEniCS III
May 6, 2015
4/7
Newton’s method (Newton-Kantorovitch)
Default for solving nonlinear problems: Newton’s method.
Newton’s method in function spaces consists of the iteration:
uk+1 = uk − J (uk )−1 F(uk ),
where J (uk ) is the Fréchet derivative of the nonlinear operator at uk .
Usually, coding the Jacobian is a major headache. In FEniCS, it is not:
J = derivative(F, u)
This is done for you automatically inside the solve.
P. E. Farrell (Oxford)
FEniCS III
May 6, 2015
5/7
p-Laplace in FEniCS: implementation
from dolfin import *
mesh = UnitSquareMesh(32, 32)
V = FunctionSpace(mesh, "Lagrange", 1)
u = interpolate(Expression("x[0]*(1-x[0])*x[1]*(1-x[1])"), V)
v = TestFunction(V)
f = Constant(1.0); g = Constant(0.0)
epsilon = Constant(1.0e-5); p = Constant(3)
bc = DirichletBC(V, g, DomainBoundary())
def gamma(u):
return (epsilon**2 + 0.5 * inner(grad(u), grad(u)))**((p-2)/2)
F = inner(grad(v), gamma(u) * grad(u))*dx - inner(f, v)*dx
solve(F == 0, u, bc)
plot(u, interactive=True)
P. E. Farrell (Oxford)
FEniCS III
May 6, 2015
6/7
FEniCS 03 Challenge!
I Starting from a good initial guess is crucial for nonlinear problems.
I What happens if a zero initial guess is used?
I Try increasing the maximum number of iterations allowed:
solve(F == 0, u, bc, solver_parameters={"newton_solver": {"maximum_iterations": 100}})
I Change p to 5. What happens now?
I A common technique for solving nonlinear problems is continuation:
I parameterise the problem with a parameter (e.g. p)
I start with a simple problem (e.g. p = 2)
I use that answer as the initial guess for the next problem
(e.g. p ← p + 1)
I repeat until you’ve solved the problem of interest.
I Starting from a zero initial guess, can you use continuation to solve
p = 5 in fewer linear solves than tackling it directly?
P. E. Farrell (Oxford)
FEniCS III
May 6, 2015
7/7
Implementing finite element models in Python/FEniCS:
coupled PDEs
Patrick Farrell, Marie Rognes, Garth Wells
MMSC: Python in Scientific Computing
June 10, 2015
P. E. Farrell (Oxford)
FEniCS IV
June 10, 2015
1 / 10
P. E. Farrell (Oxford)
FEniCS IV
June 10, 2015
2 / 10
George Stokes
I Born in Sligo, Ireland
I Lucasian Professor of Mathematics,
Cambridge, 1849–1903
I Member of Parliament, 1887–1892
I President of the Royal Society,
1885–1890
I Fundamental contributions to
incompressible flow, optics, geodesy,
spectroscopy, . . .
P. E. Farrell (Oxford)
FEniCS IV
June 10, 2015
3 / 10
The Stokes equations
We consider the stationary Stokes equations: find the velocity u and the
pressure p such that
−∇ · (2ν(u) − p I) = f
in Ω
∇ · u = 0 in Ω
where (u) =
1
2
∇u + (∇u)T and with boundary conditions
u = 0 on ∂ΩD
−(2ν − p I) · n = p0 n
on ∂ΩN
If viscosity ν varies with u (or p),
ν = ν(u)
this is a nonlinear system of partial differential equations.
P. E. Farrell (Oxford)
FEniCS IV
June 10, 2015
4 / 10
The Stokes equations: variational formulation
Assume that u ∈ V and p ∈ Q, then w = (u, p) ∈ V × Q = W .
Let f = 0.
Step 1
Multiply by test functions (v, q) ∈ W and integrate first equation by parts
Z
Z
Z
2ν(u) · ∇v dx− p∇ · v dx −
(2ν(u) − p I) · n · v ds = 0
Ω
∂Ω
ZΩ
∇ · u q dx = 0
Ω
Step 2
Adding the equations and incorporating the boundary conditions we obtain: find
(u, p) ∈ W = V0 × Q such that
Z
Z
Z
Z
2ν(u) · ∇v dx −
p∇ · v dx −
∇ · u q dx +
p0 v · n ds = 0
Ω
Ω
Ω
∂ΩN
for all (v, q) ∈ W = V0 × Q where V0 = {v ∈ V such that v|∂ΩD = 0}.
P. E. Farrell (Oxford)
FEniCS IV
June 10, 2015
5 / 10
Step by step: creating mixed function spaces
Mixed function spaces are created by taking the product of simpler spaces:
V
Q
W
#
=
=
=
W
VectorFunctionSpace(mesh, "CG", 2)
FunctionSpace(mesh, "CG", 1)
MixedFunctionSpace([V, Q])
= V * Q
You can define functions on mixed spaces and split into components:
w = Function(W)
(u, p) = split(w)
... and arguments:
y = TestFunction(W)
(v, q) = split(y)
Choice of mixed function space is crucial.
P. E. Farrell (Oxford)
FEniCS IV
June 10, 2015
6 / 10
Step by step: defining a boundary condition on a subspace
Assume that we have a mixed function space:
V = VectorFunctionSpace(...)
Q = FunctionSpace(...)
W = MixedFunctionSpace([V, Q])
The subspaces of W can be retrieved using sub:
W0 = W.sub(0)
Note that W0 is not the same as V.
The following code defines a homogenous Dirichlet (boundary) condition
on the first subspace at the part where x0 = 0.
g = (0.0, 0.0)
bc = DirichletBC(W.sub(0), g, "near(x[0], 0.0)")
P. E. Farrell (Oxford)
FEniCS IV
June 10, 2015
7 / 10
Stokes: defining the variational form
Assume that we have
w = Function(W)
(u, p) = split(w)
(v, q) = TestFunctions(W)
p0 = ...; nu = ...; n = ...
We can now specify the linear form F
epsilon = sym(grad(u))
F = (2*nu*inner(epsilon, grad(v)) \
- div(u)*q - div(v)*p)*dx \
+ p0*dot(v, n)*ds
Recall that dx denotes integration over cells and ds denotes integration over
exterior (boundary) facets.
dS denotes integration over interior facets, but we won’t get to discontinuous
Galerkin discretisations in this course.
P. E. Farrell (Oxford)
FEniCS IV
June 10, 2015
8 / 10
Stokes implementation
from dolfin import *
# Define mesh and geometry
mesh = Mesh("dolphin.xml")
n = FacetNormal(mesh)
#
V
Q
W
Define Taylor--Hood function space W
= VectorFunctionSpace(mesh, "CG" , 2)
= FunctionSpace(mesh , "CG", 1)
= MixedFunctionSpace([V, Q])
# Define Function and TestFunction(s)
w = Function(W); (u, p) = split(w)
(v, q) = TestFunctions(W)
# Define viscosity and bcs
nu = Expression("0.2*(1+pow(x[1],2))", degree=2)
p0 = Expression("1.0-x[0]", degree=1)
bcs = DirichletBC(W.sub(0), (0.0, 0.0), "on_boundary && !(near(x[0], 0.0) || near(x[0], 1.0))")
# Define variational form
epsilon = sym(grad(u))
F = (2*nu*inner(epsilon, grad(v)) - div(u)*q - div(v)*p)*dx\
+ p0*dot(v,n)*ds
# Solve problem
solve(F == 0, w, bcs)
# Plot solutions
plot(u, title="Velocity", interactive=True)
plot(p, title="Pressure", interactive=True)
P. E. Farrell (Oxford)
FEniCS IV
June 10, 2015
9 / 10
FEniCS 04 Challenge!
Solve the Stokes problem on Ω defined by the dolphin.xml mesh, defined
by the following data
−∇ · (2ν(u) − p I) = 0 in Ω
∇ · u = 0 in Ω
−(2ν(u) − p I) · n = p0 n
on ∂ΩN = {(x0 , x1 )| x0 = 0 or x0 = 1}
p 0 = 1 − x0
u = 0 on ∂ΩD = ∂Ω\∂ΩN
I Consider a constant viscosity ν = 0.2, compute and plot the solutions.
I Consider the nonlinear viscosity
ν = ν(u) = 0.5(∇u · ∇u)1/(2(k−1)) , k = 4
Compute and plot the solutions.
Hint: For nonlinear problems, it is often necessary to solve a simpler
problem to generate an initial guess.
P. E. Farrell (Oxford)
FEniCS IV
June 10, 2015
10 / 10
Implementing finite element models in Python/FEniCS:
variational inequalities
Patrick Farrell
MMSC: Python in Scientific Computing
June 10, 2015
P. E. Farrell (Oxford)
FEniCS V
June 10, 2015
1/5
Obstacle problems
Suppose an elastic membrane is attached to a flat wire frame which
encloses a region Ω of the plane. Suppose this membrane is subject to a
distributed load f (x, y). Then the equilibrium position z = u(x, y) satisfies
−∇2 u = f,
u = 0 on ∂Ω.
P. E. Farrell (Oxford)
FEniCS V
June 10, 2015
2/5
Obstacle problems
Suppose an elastic membrane is attached to a flat wire frame which
encloses a region Ω of the plane. Suppose this membrane is subject to a
distributed load f (x, y). Then the equilibrium position z = u(x, y) satisfies
−∇2 u = f,
u = 0 on ∂Ω.
Now suppose that an obstacle is placed underneath the membrane. The
obstacle z = ψ(x, y) is continuous, differentiable and ψ|∂Ω ≤ 0. The task
is to find a region R and solution u such that u coincides with ψ on R,
and u satisfies the PDE on Ω \ R.
P. E. Farrell (Oxford)
FEniCS V
June 10, 2015
2/5
Variational formulation
Let
Kψ = {v ∈ H01 (Ω) | v ≥ φ}.
Then the variational formulation is a variational inequality: find u ∈ Kψ
such that
Z
Z
f (v − u) ∀ v ∈ Kψ .
∇u · ∇(v − u) ≥
Ω
P. E. Farrell (Oxford)
Ω
FEniCS V
June 10, 2015
3/5
Variational formulation
Let
Kψ = {v ∈ H01 (Ω) | v ≥ φ}.
Then the variational formulation is a variational inequality: find u ∈ Kψ
such that
Z
Z
f (v − u) ∀ v ∈ Kψ .
∇u · ∇(v − u) ≥
Ω
Ω
Note that the scalar VI
f 0 (u)(x − u) ≥ 0 ∀ x ∈ I = [l, u]
is equivalent to: exactly one of the following conditions must hold:
I If l < x < u, then f 0 (x) = 0;
I If l = x, then f 0 (x) ≥ 0;
I If x = u, then f 0 (x) ≤ 0.
P. E. Farrell (Oxford)
FEniCS V
June 10, 2015
3/5
Useful FEniCS hints
The code
solve(F == 0, u, bc)
is equivalent to
problem = NonlinearVariationalProblem(F, u, bc,
J=derivative(F, u))
solver = NonlinearVariationalSolver(problem)
solver.solve()
You can impose bounds on the solution with
solver.parameters["nonlinear_solver"] = "snes"
solver.parameters["snes_solver"]["linear_solver"] = "lu"
solver.parameters["snes_solver"]["method"] = "vinewtonrsls"
solver.solve(lower, upper)
P. E. Farrell (Oxford)
FEniCS V
June 10, 2015
4/5
FEniCS 05 Challenge!
Solve the Poisson obstacle problem with Ω = [−2, 2]2 , f = 0 and
(p
1 − x2 − y 2 if x2 + y 2 < 1
ψ(x, y) =
0
otherwise.
P. E. Farrell (Oxford)
FEniCS V
June 10, 2015
5/5
Implementing finite element models in Python/FEniCS:
steady nonlinear vector-valued PDEs
Patrick Farrell, Anders Logg, Marie Rognes
MMSC: Python in Scientific Computing
May 7, 2015
P. E. Farrell (Oxford)
FEniCS VI
May 7, 2015
1/8
Static hyperelasticity
−∇ · P = B
in Ω
u=g
on ΓD
P ·n=T
on ΓN
I u is the displacement (vector-valued)
I P = P (u) is the first Piola–Kirchhoff stress tensor
I B is a given body force per unit volume
I g is a given boundary displacement
I T is a given boundary traction
P. E. Farrell (Oxford)
FEniCS VI
May 7, 2015
2/8
Variational problem
Multiply by a test function v ∈ V̂ and integrate by parts:
Z
Z
Z
P : ∇v dx −
(P · n) · v ds
− ∇ · (P · v) dx =
Ω
Ω
∂Ω
Note that v = 0 on ΓD and P · n = T on ΓN
Find u ∈ V such that
Z
Z
Z
P : ∇v dx =
B · v dx +
Ω
Ω
T · v ds
ΓN
for all v ∈ V̂
P. E. Farrell (Oxford)
FEniCS VI
May 7, 2015
3/8
Stress–strain relations
I F = I + ∇u is the deformation gradient
I C = F > F is the right Cauchy–Green tensor
I E = 12 (C − I) is the Green–Lagrange strain tensor
I W = W (E) is the strain energy density
I Sij =
∂W
∂Eij
is the second Piola–Kirchhoff stress tensor
I P = F S is the first Piola–Kirchhoff stress tensor
St. Venant–Kirchhoff strain energy function:
W (E) =
P. E. Farrell (Oxford)
λ
(tr(E))2 + µ tr(E 2 )
2
FEniCS VI
May 7, 2015
4/8
Useful FEniCS tools (I)
Loading a mesh from a file:
mesh = Mesh("whatever.xml")
Vector-valued function spaces:
V = VectorFunctionSpace(mesh, "Lagrange", 1)
Vector-valued Constants:
g = Constant((0, 0, -9.81)) # accel. due to gravity
P. E. Farrell (Oxford)
FEniCS VI
May 7, 2015
5/8
Useful FEniCS tools (II)
Defining subdomains/boundaries:
class MyBoundary(SubDomain):
def inside(self, x, on_boundary):
return on_boundary and near(x[0], 0.0)
Marking boundaries:
my_boundary_1 = MyBoundary1()
my_boundary_2 = MyBoundary2()
boundaries = FacetFunction("uint", mesh)
boundaries.set_all(0)
my_boundary_1.mark(boundaries, 1)
my_boundary_2.mark(boundaries, 2)
ds = ds[boundaries]
F = ...*ds(0) + ...*ds(1)
P. E. Farrell (Oxford)
FEniCS VI
May 7, 2015
6/8
Useful FEniCS tools (III)
Computing derivatives of expressions:
I =
F =
C =
...
E =
W =
S =
P =
Identity(3)
I + grad(u)
F.T * F
variable(...)
...
diff(W, E)
F*S
Computing functionals of a solution:
J = assemble(u[0]*dx) / assemble(Constant(1)*dx, mesh=mesh)
P. E. Farrell (Oxford)
FEniCS VI
May 7, 2015
7/8
FEniCS 06 Challenge!
Compute the deflection of a regular 10 × 2 LEGO brick. Use the
St. Venant–Kirchhoff model and assume that the LEGO brick is made of
PVC plastic. The LEGO brick is subject to gravity of size g = −9.81 m/s2
and a downward traction of size 5000 N/m2 at its end point.
g = −9.81m/s2
T = 5000 N/m2
Compute the average value of the displacement in the z-direction.
Mesh and material parameters: http://www.maths.ox.ac.uk/courses/course/26423
Submit your answers (one real number) to
[email protected] .
P. E. Farrell (Oxford)
FEniCS VI
May 7, 2015
8/8
Implementing finite element models in Python/FEniCS:
Navier-Stokes
Patrick Farrell, Anders Logg, André Massing
MMSC: Python in Scientific Computing
May 7, 2015
P. E. Farrell (Oxford)
FEniCS VII
May 7, 2015
1 / 11
The incompressible Navier–Stokes equations
ρ(u̇ + u · ∇u) − ∇ · σ(u, p) = f
in Ω × (0, T ]
∇·u=0
in Ω × (0, T ]
u = gD
on ΓD × (0, T ]
σ · n = gN
on ΓN × (0, T ]
u(·, 0) = u0
I
I
I
I
I
I
I
I
in Ω
u is the fluid velocity and p is the pressure
ρ is the fluid density
σ(u, p) = 2µ(u) − pI is the Cauchy stress tensor
(u) = 12 (∇u + (∇u)> ) is the symmetric gradient
f is a given body force per unit volume
gD is a given boundary displacement
gN is a given boundary traction
u0 is a given initial velocity
P. E. Farrell (Oxford)
FEniCS VII
May 7, 2015
2 / 11
Variational problem
Multiply the momentum equation by a test function v and integrate by
parts:
Z
Z
Z
Z
ρ(u̇ + u · ∇u) · v dx +
σ(u, p) : (v) dx =
f · v dx +
gN · v ds
Ω
Ω
Ω
ΓN
Short-hand notation:
hρu̇, vi + hρu · ∇u, vi + hσ(u, p), (v)i = hf, vi + hgN , viΓN
Multiply the continuity equation by a test function q and sum up: find
(u, p) ∈ V such that
hρu̇, vi + hρu · ∇u, vi + hσ(u, p), (v)i + h∇ · u, qi = hf, vi + hgN , viΓN
for all (v, q) ∈ V̂
P. E. Farrell (Oxford)
FEniCS VII
May 7, 2015
3 / 11
Discrete variational problem
Time-discretization leads to a saddle-point problem on each time step:
M + ∆tA + ∆tN (U ) ∆tB
U
b
=
P
0
∆tB >
0
I Efficient solution of the saddle-point problem relies on the efficiency
of special-purpose preconditioners (Uzawa iteration, Schur
complement preconditioners, . . . ).
I We will use a splitting approach (more complicated assembly, easier
linear systems).
P. E. Farrell (Oxford)
FEniCS VII
May 7, 2015
4 / 11
The classical Chorin-Temam projection method
I Step 1: Compute tentative velocity uF solving
uF − un
− ν∆uF + (u∗ · ∇)u∗∗ = f n+1
∆t
uF = gD
∂uF
=0
∂n
in Ω
on ΩD
on ΩN
I Step 2: Compute a corrected velocity un+1 and a new pressure pn+1
solving
un+1 − uF
+ ∇pn+1 = 0 in Ω
∆t
∇ · un+1 = 0
in Ω
un+1 · n = 0
P. E. Farrell (Oxford)
FEniCS VII
on ∂Ω
May 7, 2015
5 / 11
Computing the tentative velocity
In principle, the term (u∗ · ∇)u∗∗ can be approximated in several ways
I Explicit: u∗ = u∗∗ = un ⇒ diffusion-reaction equation
I Semi-implicit u∗ = un and u∗∗ = un+1 ⇒ advection-diffusion-reaction
equation
I Fully-implicit u∗ = u∗∗ = un+1 retaining the basic non-linearity in the
Navier-Stokes equations
The natural outflow condition ν∂n u − pn = 0 is artificially enforced by
requiring
I ∂n uF = 0 on ∂ΩN in step 1
I pn+1 = 0 on ∂ΩN in step 2
P. E. Farrell (Oxford)
FEniCS VII
May 7, 2015
6 / 11
Solving the projection step
un+1 − uF
+ ∇pn+1 = 0 and using requirement
∆t
= 0 yields
Applying ∇· to
∇ · un+1
∆pn+1 =
1
∇ · uF
∆t
in Ω
We already required
p = 0 on ∂ΩN
Multiplying
un+1
−
∆t
uF
+ ∇pn+1 = 0 with n and restricting to ∂ΩD gives
∂pn+1
= 0 on ∂ΩD
∂n
Compute un+1 by
un+1 = uF − ∆t∇pn+1
including boundary conditions for u at t = tn+1
P. E. Farrell (Oxford)
FEniCS VII
May 7, 2015
7 / 11
Chorin-Teman projection method – Summary
(1) Compute tentative velocity uF by
(
uF − un
, v) + ((u∗ · ∇)u∗∗ , v) + ν(∇uF , ∇v) − (f, v) = 0
∆t
including boundary conditions for the velocity.
(2) Compute new pressure pn+1 by
(∇pn+1 , ∇q) +
1
(∇ · uF , q) = 0
∆t
including boundary conditions for the pressure.
(3) Compute corrected velocity by
(un+1 − uF , v) + ∆t(∇pn+1 , v) = 0
including boundary conditions for the velocity.
P. E. Farrell (Oxford)
FEniCS VII
May 7, 2015
8 / 11
Useful FEniCS tools
Advection term:
F = inner(v, grad(u)*u)*dx + ...
P. E. Farrell (Oxford)
FEniCS VII
May 7, 2015
9 / 11
Useful FEniCS tools
Apply C++ optimisation flags to generated assembly code:
parameters["form_compiler"]["cpp_optimize"] = True
parameters["form_compiler"]["cpp_optimize_flags"] = \
"-O3 -ffast-math -march=native"
Running in parallel:
$ mpiexec -n 4 python navier_stokes.py
P. E. Farrell (Oxford)
FEniCS VII
May 7, 2015
10 / 11
FEniCS 07 Challenge!
Solve the time-dependent Navier-Stokes equation on the domain Ω defined
by cylinder.xml.
Data: ν = 0.001 m2 /s, ρ = 1.0 kg/m3 , T = 8 s, ∆t = 0.001 s, f = 0.
Boundary conditions:
I Inflow on the left given by
U = 4Um y(H − y) sin(πt/8)/H 2 ,
V = 0.
with Um = 1.5 m/s, H = ??? m.
I Outflow on the right.
I No-slip elsewhere.
This is the famous benchmark of Schäfer and Turek (1996).
P. E. Farrell (Oxford)
FEniCS VII
May 7, 2015
11 / 11
Implementing finite element models in Python/FEniCS:
optimal control
Patrick Farrell, Simon Funke
MMSC: Python in Scientific Computing
June 10, 2015
P. E. Farrell (Oxford)
FEniCS VIII
June 10, 2015
1 / 11
What is PDE-constrained optimisation?
Optimisation problems where at least one constraint is a partial differential
equation.
Applications
I Shape and topology optimisation (e.g. optimal shape of a wing)
I Data assimilation (e.g. weather prediction)
I Inverse problems (e.g. petroleum exploration)
I ...
P. E. Farrell (Oxford)
FEniCS VIII
June 10, 2015
2 / 11
Hello World of PDE-constrained optimisation
We will solve an optimisation problem involving the Poisson equation:
1
min
u,m 2
subject to
Z
α
(u − ud ) dx +
2
Ω
−∆u = m
u = u0
2
Z
m2 dx
Ω
in Ω
on ∂Ω
This problem can be physically interpreted as: find the heating/cooling
term m for which u best approximates the desired heat distribution ud .
The regularisation term in the functional ensures existence and uniqueness
for α > 0.
P. E. Farrell (Oxford)
FEniCS VIII
June 10, 2015
3 / 11
The canonical abstract form
min J (u, m)
u,m
subject to:
F(u, m) = 0,
with
I the objective functional J .
I the parameter m.
I the PDE operator F with solution u ∈ U, parametrised by m ∈ M.
P. E. Farrell (Oxford)
FEniCS VIII
June 10, 2015
4 / 11
Oneshot solution strategy
We form the Lagrangian L:
L(u, λ, m) = J (u, m) + λ∗ F (u, m)
Optimality conditions (Karush-Kuhn-Tucker): ∇L = 0 at an optimum:
dL
= 0,
du
dL
= 0,
dλ
dL
= 0.
dm
Oneshot approach: solve these three (coupled, often nonlinear) PDEs
together.
P. E. Farrell (Oxford)
FEniCS VIII
June 10, 2015
5 / 11
Comments on oneshot approach
The oneshot approach can be extremely fast, but very difficult to
converge. (Very difficult to ensure convergence of Newton’s method, and
to solve the resulting linear systems.)
Oneshot approaches tend to work best for steady PDEs, reduced
approaches tend to work best for time-dependent PDEs. (Reduced
approaches are outside the scope of the course.)
P. E. Farrell (Oxford)
FEniCS VIII
June 10, 2015
6 / 11
Forming the Lagrangian
L(u, λ, m) =
1
2
Z
Ω
(u − ud )2 dx +
α
2
Z
m2 dx +
Ω
V =
Z =
z =
(u,
FunctionSpace(mesh, "Lagrange", 1)
MixedFunctionSpace([V, V, V])
Function(Z)
lmbd, m) = split(z)
L =
+
+
-
0.5*inner(u-ud, u-ud)*dx
0.5*alpha*inner(m, m)*dx
inner(grad(u), grad(lmbda))*dx
m*lmbda*dx
P. E. Farrell (Oxford)
FEniCS VIII
Z
∇λ · ∇u − λm dx
Ω
June 10, 2015
7 / 11
KKT conditions
−∇2 u = m
−∇2 λ = J 0
αm = λ
kkt = derivative(L, z, TestFunction(Z))
P. E. Farrell (Oxford)
FEniCS VIII
June 10, 2015
8 / 11
Implementation
from dolfin import *
# Define discrete Functionspace
mesh = UnitSquareMesh(100, 100)
U = FunctionSpace(mesh, "CG", 1) # Space for forward solution
V = FunctionSpace(mesh, "CG", 1) # Space for adjoint solution
M = FunctionSpace(mesh, "DG", 0) # Space for control
Z = MixedFunctionSpace([U, V, M])
# Define Functions
z = Function(Z)
(u, lmbda, m) = split(z)
# Define variational problem
F = inner(grad(u), grad(lmbda))*dx - m*lmbda*dx
# Define functional
ud = Expression("sin(pi*x[0])*sin(pi*x[1])")
# Desired temperature profile
J = inner(u-ud, u-ud)*dx + Constant(1e-6)*inner(m, m)*dx
# Define boundary conditions
bc_u
= DirichletBC(Z.sub(0), 0.0, "on_boundary")
bc_lmbd = DirichletBC(Z.sub(1), 0.0, "on_boundary")
bcs = [bc_u, bc_lmbd]
# Derive optimality conditions
L = J + F
kkt = derivative(L, z, TestFunction(Z))
# Solve Poisson problem
solve(kkt == 0, z, bcs)
P. E. Farrell (Oxford)
FEniCS VIII
June 10, 2015
9 / 11
FEniCS 08 Challenge!
In http://dx.doi.org/10.1137/S0363012994261707, Ito and Kunisch
consider the following optimal control problem:
α
1
ku − ud k2L2 (Ω) + kmk2L2 (Γ)
2
2
2
3
subject to −∇ u + u − u = 0
on Ω,
min
u,m
∇u · n = m
on Γ.
This Ginzburg-Landau PDE arises in superconductivity. Solve this optimal
control problem with Ω = [0, 1] × [0, 2], α = 10−7 , ud = 3.
Hint: we can’t represent functions on trace spaces yet (i.e. only on the
boundary), so your m needs to live in a volume function space. Modify the
regularisation term appropriately.
P. E. Farrell (Oxford)
FEniCS VIII
June 10, 2015
10 / 11
Final Challenge!
MMSC: for this to count for your MSc, you have to write a final report.
Your task:
I Implement a finite element discretisation of a PDE.
I Take a discretisation and configuration from the literature, or invent
your own.
I Bonus points for: nonlinearity, coupling, mathematical interest, . . .
I Include an MMS verification.
I Submit working code and a report (≤ 20 pages) discussing the PDE
and its implementation.
I Inspiration: your summer projects; the PDE coffee table book.
I Deadline: 6 July 2015.
P. E. Farrell (Oxford)
FEniCS VIII
June 10, 2015
11 / 11