I am working on the file "Lecture Notes/ModelFitting.ipynb".
The first task is:
Oops what happened? Well, the model value at x=0 is 0 in this case, and the errors are too, so our 1/errors_poissson statement becomes problematic because we can't divide by zero.
We can fix this by removing the datapoint from consideration (indeed it's rare that we measure something to be zero anyway, so it was a bit contrived to begin with).
Can you write Python code for it?

x3=np.arange(10)+1
model3=2*x3
noise3 = (np.random.rand(10)-0.5)*10
data3= 2*x3+noise3
errors_poisson3 = np.sqrt(data3)

lsq_weighted=sum(1/errors_poisson3**2*(data3-model3)**2)
lsq_weighted


I am working on the file "Lecture Notes/ModelFitting.ipynb".
The first task is:
Overfitting
since we can define functions to arbitrary dimensions, this can get a bit out of control
Can you write Python code for it?

def tenparamfunc(x,a,b,c,d,e,f,g,h,i,j):
    return a+b*x+c*x**2+d*x**3+e*x**4+f*x**5+g*x**6+h*x**7+i*x**8+j*x**9

fit2 = optimization.curve_fit(tenparamfunc,x,data)
fit2[0]

plt.plot(x,2*x+noise, 'go')
c = fit2[0]
plt.plot(x, tenparamfunc(x,c[0],c[1],c[2],c[3],c[4],c[5],c[6],c[7],c[8],c[9]))
plt.xlabel("independent variable")
plt.ylabel("dependent variable")
plt.title("fit for function with ten parameters")


I am working on the file "Lecture Notes/ModelFitting.ipynb".
The first task is:
Weighted Least Squares
If we want to take the uncertainty in each of our data points into consideration in calculating goodness of fit, we can extend this to assigning "weights" to each data point.
Since larger error bars indicate greater uncertainty, these data points should be assigned less weight than other data points with smaller error bars.
A weight is just like a coefficient in front of the (data-model)$^2$ calculation typical to least squares. More formally:
$$ Q = \sum_{i=1}^nw_i[y_i-f(x_i,\beta)]^2$$
Where $x_i$ is the independent variable, $y_i$ are the observed values, $f(x_i,\beta)$ is the model with some set of parameters $\beta$ and $w_i$ are the weights for each datapoint
A common weight is the reciprocal of the error value squared, or $\frac{1}{\sigma^2}$. Sigma here is the value of the error bar and is not to be confused with a standard deviation, though standard deviation values are often assigned as errors.
Let's do this for our example of heteroschedastic error bars above
Can you write Python code for it?


I am working on the file "Lecture Notes/ModelFitting.ipynb".
The first task is:
this is usually called "least-squares" fitting
Curve Fitting
Can you write Python code for it?

#python has lots of built-in functionalities for this kind of thing, but we'll use the scipy optimize curve_fit function
import scipy.optimize as optimization

#to use it, you have to define a functional form for the fit line BUT NOT THE SPECIFIC VALUES

#for linear (straight line) fits this could take two forms

#line without an intercept (intercept zero)
def slopefunc(x,sl):
    return sl*x

#line with an intercept
def slopeintfunc(x,sl,incpt):
    return sl*x+incpt

#we could continue this to functions of arbitraty order
#for example, quadratic:

def quadfunc(x,a,b,c):
    return a+b*x+c*x*x

#then use curve_fit
fit = optimization.curve_fit(slopeintfunc,x,data)

#the zeroth element then contains the optimal parameters for the functional parameters (in this case sl, incpt)
fit[0]

#and the next element contains what's called the covariance matrix
fit[1]

#let's plot it over the data now
plt.plot(x,2*x+noise, 'go')
plt.plot(x, slopeintfunc(x,fit[0][0],fit[0][1]))
plt.xlabel("independent variable")
plt.ylabel("dependent variable")
plt.title("least squares fit")


I am working on the file "Lecture-09-Matrices-and-Rotations.ipynb".
The first task is:
A collection of functions for performing transformations is available at http://www.lfd.uci.edu/~gohlke/.  This can be imported and the functions used in your code.  The fastest way to explore what is available is to import the file and then use autocomplete and docstring viewing functions from the Jupyter notebook.
Can you write Python code for it?

import transformations as tfm

tfm.rotation_matrix(np.pi/2, [0,1,0])

zeroArray(tfm.rotation_matrix(np.pi/2, [0,1,0]))


I am working on the file "Lecture-09-Matrices-and-Rotations.ipynb".
The first task is:
Advanced Topics:  The Metric Tensor
Students who learn crystallography for the first time are introduced to the topic through study of cubic crystal structures.  This permits an appeal to our intuition about orthonormal (Euclidian) coordinate systems.  This approach misses out on a more general method for teaching the topic where the d-spacing and the angle between directions can be worked out for any general crystal system.  The method for describing distances in a general reference frame involves the metric tensor.  The metric tensor defines how distances are measured in every direction and its components are the dot product of every combination of basis vector in the system of interest.  We use the standard lattice parameter designations:
$$
[a, b, c, \alpha, \beta, \gamma]
$$
where $\gamma$ is the angle between $\mathbf{a}$ and $\mathbf{b}$, etc.  This general system has basis vectors:
$$
\mathbf{a},\mathbf{b}, \mathbf{c}
$$
The standard geometric interpretation of an inner product of vectors is:
$$
\mathbf{a} \cdot \mathbf{b} = |a| \; |b| \; \cos{\gamma}
$$
so that the metric tensor is:
$$
g_{ij} =
\begin{bmatrix}
\mathbf{a} \cdot \mathbf{a} &
\mathbf{a} \cdot \mathbf{b} &
\mathbf{a} \cdot \mathbf{c} \
\mathbf{b} \cdot \mathbf{a} &
\mathbf{b} \cdot \mathbf{b} &
\mathbf{b} \cdot \mathbf{c} \
\mathbf{c} \cdot \mathbf{a} &
\mathbf{c} \cdot \mathbf{b} &
\mathbf{c} \cdot \mathbf{c}
\end{bmatrix}
$$
Committing this to memory is simple and it has an intuitive meaning in that each entry measures a different projection of a vector component onto an axis in a general crystal system within lattice coordinates.  It is possible to write a small function in SymPy to illustrate this.
Can you write Python code for it?

def metricTensor(a=1, b=1, c=1, alpha=sp.pi/2, beta=sp.pi/2, gamma=sp.pi/2):
    return sp.Matrix([[a*a, a*b*sp.cos(gamma), a*c*sp.cos(beta)], \
                     [a*b*sp.cos(gamma), b*b, b*c*sp.cos(alpha)], \
                     [a*c*sp.cos(beta), b*c*sp.cos(alpha), c*c]])

sp.var('a b c alpha beta gamma u v w h k l')

M = metricTensor(a=a,
                 b=b,
                 c=c,
                 alpha=alpha,
                 beta=beta,
                 gamma=gamma
                )
M


I am working on the file "Lecture-09-Matrices-and-Rotations.ipynb".
The first task is:
Advanced Topics: Euler Angles
This discussion is derived primarily from Arfken, Chapter 3.3.  The figure below is from Arfken:

There are three successive rotations used in the Euler angle formalism - the product of these three rotations is the single operation that transforms one set of coordinates $(x,y,z)$ into another, $(x',y',z')$.  The order is important and is the difference between active and passive rotations.
In steps as shown in Figure 3.7 from Arfken:

The first rotation is about $x_3$.  In this case the $x'_3$ and $x_3$ axes coincide.  The angle $\alpha$ is taken to be positive (counterclockwise).  Our new coordinate system is $(x'_1,x'_2,x'_3)$.
The coordinates are now rotated through an angle $\beta$ around the $x'_2$ axis.  Our new coordinate system is now $(x''_1,x''_2,x''_3)$.
The final rotation is through the angle $\gamma$ about the $x'''_3$ axis.  Our coordinate system is now the $(x'''_1,x'''_2,x'''_3)$.  In the case pictured above the $x''_3$ and $x'''_3$ axes coincide.

For example:
Can you write Python code for it?

alpha, beta, gamma = sp.symbols('alpha beta gamma')

def rZ(angle):
    sa = sp.sin(angle)
    ca = sp.cos(angle)
    M = sp.Matrix([[ca, sa, 0],
                  [-sa, ca, 0],
                  [0, 0, 1]])
    return M

def rY(angle):
    sb = sp.sin(angle)
    cb = sp.cos(angle)
    M = sp.Matrix([[cb, 0, -sb],
                  [0, 1, 0],
                  [sb, 0, cb]])
    return M

(rZ(alpha), rY(beta), rZ(gamma))


I am working on the file "Lecture-09-Matrices-and-Rotations.ipynb".
The first task is:
DIY:  Compute the angle between the $(123)$ plane and the $(112)$ plane in a trigonal crystal system

The trigonal crystal system has the least symmetry.  Refer to standard texts for the pattern of lattice parameters.
Can you write Python code for it?

vectorOne = np.array([1,2,3])
vectorTwo = np.array([1,1,2])

# Put your code here.


I am working on the file "Lecture-09-Matrices-and-Rotations.ipynb".
The first task is:
Helper Classes, Drawing and Plane Groups

Let us explore a bit of how we can draw an image - and then we have everything we need to start building the plane group representations.  To build pictoral representations of plane groups, two classes have been created to simplify the organization of the motif.
Below are the Point and Triangle classes for your use.  A Point has storage for an $(x,y)$ position.  Rather than building arrays and referencing specific positions within the array a more natural referencing of points is possible.  If we define a point p1 = Point(10,20) we can access the points by p1.x and p1.y.  The code is more easily read and debugged with this syntax.
Building on the Point class we define a Triangle class.  The Triangle permits access of each point and defines an affine() method that will take a Numpy array that represents a transformation matrix.  This method returns a new instance of a Triangle and preserves the original points.  Writing the code this way avoids explicit handling of the transformation matrices.
These two classes and methods are demonstrated in the second and third code blocks.  Building on this demonstration you will be able to complete the homework.
Can you write Python code for it?

# Class definitions

from math import sqrt
import numpy as np

class Point:
    """
    A Point object to simplify storage of (x,y) positions.
    p1.x, p1.y, etc...
    """
    def __init__(self,x_init,y_init):
        self.x = x_init
        self.y = y_init

    def shift(self, x, y):
        self.x += x
        self.y += y

    def __repr__(self):
        return "".join(["Point(", str(self.x), ",", str(self.y), ")"])

class Triangle:
    """
    A Triangle class constructed from points.  Helps organize information on
    triangles.  Has a points() method for returning points in a form that
    can be used with polygon drawing from Python Image Library (PIL) and a
    method, affine(), that applies a matrix transformation to the points.
    """
    def __init__(self, p1_init, p2_init, p3_init):
        self.p1 = p1_init
        self.p2 = p2_init
        self.p3 = p3_init

    def points(self):
        x1, y1 = self.p1.x, self.p1.y
        x2, y2 = self.p2.x, self.p2.y
        x3, y3 = self.p3.x, self.p3.y

        return [(x1,y1),(x2,y2),(x3,y3)]

    def affine(self, affineMatrix):
        """
        Applies an affine transformation to a triangle and changes the
        points of the triangle.  This code returns a new triangle.  Uses
        Points to simplify augmenting the matrix and dot products.
        """
        x1, y1 = self.p1.x, self.p1.y
        x2, y2 = self.p2.x, self.p2.y
        x3, y3 = self.p3.x, self.p3.y

        p1Vector = np.array([[x1, y1, 1]])
        p2Vector = np.array([[x2, y2 , 1]])
        p3Vector = np.array([[x3, y3, 1]])

        p1New = np.dot(affineMatrix, p1Vector.T)
        p2New = np.dot(affineMatrix, p2Vector.T)
        p3New = np.dot(affineMatrix, p3Vector.T)

        # This line needs to be cleaned up.
        newTriangle = Triangle(Point(p1New[0,0],p1New[1,0]),Point(p2New[0,0],p2New[1,0]),Point(p3New[0,0],p3New[1,0]))

        return newTriangle


I am working on the file "Lecture-09-Matrices-and-Rotations.ipynb".
The first task is:
Rotations (Continued)

Using the new rotation2D function and zeroArray function we can now write:
Can you write Python code for it?


I am working on the file "Lecture-09-Matrices-and-Rotations.ipynb".
The first task is:
The key is in the Boolean comparision using the < symbol.  The expression returns a numpy array of dtype=bool.  Let me say here that it is good to check the results of expressions if you are unsure.
Can you write Python code for it?


I am working on the file "Lecture-09-Matrices-and-Rotations.ipynb".
The first task is:
Using the Python Imaging Library

In order that our transformations can be visualized, we will use the Python Imaging Library and the polygon() method.  The polygon() method takes a list of points in the format returned by our Triangle class method points().  The code below is a very simple starting point for the student to begin building a representation of the plane groups.
Can you write Python code for it?

# Class demonstrations

%matplotlib inline

import numpy as np
import math
from PIL import Image, ImageDraw

image = Image.new('RGB', (500, 500), 'white')
draw = ImageDraw.Draw(image)

t1 = Triangle(Point(10,10),Point(40,10),Point(10,50))
t2 = Triangle(Point(10,10),Point(40,10),Point(10,50))
translationMatrix = np.array([[1,0,50],[0,1,0],[0,0,1]])
reflectionMatrix = np.array([[-1,0,0],[0,1,0],[0,0,1]])

t2 = t1.affine(reflectionMatrix*translationMatrix)

print(t1.points(), t2.points())

draw.polygon(t1.points(), outline='black', fill='red')
draw.polygon(t2.points(), outline='black', fill='green')

image

# A slightly more advanced demonstration

%matplotlib inline

import numpy as np
import math
from PIL import Image, ImageDraw

image = Image.new('RGB', (500, 500), 'white')
draw = ImageDraw.Draw(image)

t1 = Triangle(Point(70,10),Point(100,10),Point(70,50))

triangleList = [t1]

translationMatrix = np.array([[1,0,10],[0,1,0],[0,0,1]])
reflectionMatrixY = np.array([[-1,0,0],[0,1,0],[0,0,1]])
reflectionMatrixX = np.array([[1,0,0],[0,-1,0],[0,0,1]])

r90 = np.array([[0,-1,0],[1,0,0],[0,0,1]])

triangleList.append(t1.affine(r90))
triangleList.append((t1.affine(r90)).affine(r90))
triangleList.append((t1.affine(r90)).affine(r90).affine(r90))

tempList = [triangle.affine(reflectionMatrixX) for triangle in triangleList]
triangleList.extend(tempList)

# Using an affine transformation to center the triangles in the drawing
# as canvas coordinates are (0,0) at the top left.
centerMatrix = np.array([[1,0,250],[0,1,250],[0,0,1]])
drawList = [triangle.affine(centerMatrix) for triangle in triangleList]
for triangle in drawList:
    draw.polygon(triangle.points(), outline='black', fill='red')

image


I am working on the file "Lecture-09-Matrices-and-Rotations.ipynb".
The first task is:
We can look up definitions, but we can also do some simple tests to see which way things rotate.  Let us take a vector pointing in the $\mathbf{x}$ direction and rotate it about $\mathbf{z}$ by 90 degrees and see what happens:
Can you write Python code for it?

xUnit = sp.Matrix([1,0,0])
zRotation = sp.rot_axis3(sp.rad(90))

xUnit

# Each column can be viewed as where the unit vectors are moved to in the new space.
zRotation

zRotation*xUnit

# This should not work.
xUnit*zRotation

xUnit.T*zRotation


I am working on the file "Lecture-09-Matrices-and-Rotations.ipynb".
The first task is:
We can write a function to do this that is a bit more robust.  Modifications are done in-place (by reference) so we just return the array passed to the function after some manipulation that we do by Boolean indexing.
Can you write Python code for it?

def zeroArray(testArray):
    testArray[np.isclose(testArray, np.zeros_like(testArray))] = 0.0
    return testArray

modifiedArray = rotation2D(np.pi/2)
modifiedArray

modifiedArray = zeroArray(rotation2D(np.pi/2))
modifiedArray


I am working on the file "Lecture-09-Matrices-and-Rotations.ipynb".
The first task is:
You'll find that the symbolic triple matrix product matches up with the results in Arfken for the definition of Euler angles $\alpha$, $\beta$, $\gamma$.  Note also that this is much easier to compute than by hand!  Also - less likely to result in errors.
Can you write Python code for it?


I am working on the file "Lecture-10A-Taylors-Series.ipynb".
The first task is:
A list comprehension can be used to organize and extend the results further.  We can wrap the list above into another list that changes the order of differentiation each iteration.
Can you write Python code for it?

maximumOrder = 5
funcAndSeries = [[sp.diff(a,x,order) for a in [sp.sin(x),powerSeries]] for order in range(maximumOrder)]
funcAndSeries


I am working on the file "Lecture-10A-Taylors-Series.ipynb".
The first task is:
A list comprehension is used to organize the results.  In each iteration the exact function and the power series are differentiated and stored as an element of a list.  The list can be inspected and a set of simultaneous equations can be written down and solved to determine the values of the coefficients.  Casting the list as a sympy Matrix object clarifies the correspondance between entries in the list.
Can you write Python code for it?

orderOfDifferentiation = 1
powerSeries = A+B*x+C*x**2+D*x**3+E*x**4
# Differentiate, element by element, the list [sp.sin(x),powerSeries]
[sp.diff(a,x,orderOfDifferentiation) for a in [sp.sin(x),powerSeries]]


I am working on the file "Lecture-10A-Taylors-Series.ipynb".
The first task is:
A power series is of the form:
$$
\sum_{n=0}^{\infty} M_{n} x^{n} = M_0 + M_1 x + M_2 x^2 + \cdots
$$
Can you write Python code for it?

M = sp.IndexedBase('M')

sp.Sum(M[n]*x**n, (n,0,m))


I am working on the file "Lecture-10A-Taylors-Series.ipynb".
The first task is:
DIY:  Determine if the following sequences are convergent or divergent.  If convergent, what is the limit?
$$
\begin{aligned}
a_n = & \frac{1}{n} \
a_n = & 1 - (0.2)^n \
a_n = & \frac{1}{2n+1}
\end{aligned}
$$
Can you write Python code for it?


I am working on the file "Lecture-10A-Taylors-Series.ipynb".
The first task is:
DIY:  Determine the coefficients in the above power series.

You don't necessarily need to write code to complete this DIY problem.
Can you write Python code for it?

# Your code here if you feel you need it.


I am working on the file "Lecture-10A-Taylors-Series.ipynb".
The first task is:
Lecture 10:  Taylor's Series and Discrete Calculus
Background
It is common in physics and engineering to represent transcendental functions and other nonlinear expressions using a few terms from a Taylor series.  This series provides a fast and efficient way to compute quantities such as $\mathrm{sin}(x)$ or $e^x$ to a prescribed error.  Learning how to calculate the series representation of these functions will provide practical experience with the Taylor series and help the student understand the results of Python methods designed to accelerate and simplify computations.  The series can be written generally as:
$$f(x) = f{\left (0 \right )} + x \left. \frac{d}{d x} f{\left (x \right )} \right|{\substack{ x=0 }} + \frac{x^{2}}{2} \left. \frac{d^{2}}{d x^{2}}  f{\left (x \right )} \right|{\substack{ x=0 }} + \frac{x^{3}}{6} \left. \frac{d^{3}}{d x^{3}}  f{\left (x \right )} \right|{\substack{ x=0 }} + \frac{x^{4}}{24} \left. \frac{d^{4}}{d x^{4}}  f{\left (x \right )} \right|{\substack{ x=0 }} + \frac{x^{5}}{120} \left. \frac{d^{5}}{d x^{5}}  f{\left (x \right )} \right|_{\substack{ x=0 }} + \mathcal{O}\left(x^{6}\right)$$
Of equal importance the Taylor series permits discrete representation of derivatives and is a common way to perform numerical integration of partial and ordinary differential equations.  Expansion of a general function $f(x)$ about a point, coupled with algebraic manipulation, will produce expressions that can be used to approximate derivative quantities.  Although any order of derivative can be computed, this lesson will focus on first and second derivatives that will be encountered in the diffusion equation.
What Skills Will I Learn?
You will practice the following skills:

Defining and determining the limits of infinite sequences, series and power series.
Define the Taylor series and write the general form about any point and to any order.
Derive the central and forward difference formulae for numerical derivatives using the Taylor's series.

What Steps Should I Take?

Learn to use Sympy...
Can you write Python code for it?

import sympy as sp
x, y, z, t, a = sp.symbols('x y z t a')
k, m, n = sp.symbols('k m n', integer=True)
f, g, h = sp.symbols('f g h', cls=sp.Function)
sp.var('a1:6')
sp.init_printing()


I am working on the file "Lecture-10A-Taylors-Series.ipynb".
The first task is:
Meaning that:
$$
f(c+h) = \frac{h^{2}}{2} \left. \frac{d^{2}}{d \xi_{1}^{2}}  f{\left (\xi_{1} \right )} \right|{\substack{ \xi{1}=c }} + h \left. \frac{d}{d \xi_{1}} f{\left (\xi_{1} \right )} \right|{\substack{ \xi{1}=c }} + f{\left (c \right )}
$$
Can you write Python code for it?


I am working on the file "Lecture-10A-Taylors-Series.ipynb".
The first task is:
Meaning that:
$$
f(c-h) = \frac{h^{2}}{2} \left. \frac{d^{2}}{d \xi_{1}^{2}}  f{\left (\xi_{1} \right )} \right|{\substack{ \xi{1}=c }} - h \left. \frac{d}{d \xi_{1}} f{\left (\xi_{1} \right )} \right|{\substack{ \xi{1}=c }} + f{\left (c \right )}
$$
Solving for First and Second Derivatives

Inspection of the results shows that the signs on the terms containing the first derivative are different between the two expressions.  We can use this to our advantage in solving for the derivative terms explicitly.  Note that each grouped expression is equal to zero as is the default in sympy.
Find the first derivative in this expression:
Can you write Python code for it?


I am working on the file "Lecture-10A-Taylors-Series.ipynb".
The first task is:
SymPy has a function that can take SymPy expressions and represent them as power series:
Can you write Python code for it?


I am working on the file "Lecture-10A-Taylors-Series.ipynb".
The first task is:
Taylor's Series

Below we present a derivation of Taylor's series and small algebraic argument for series representations of functions.  In contrast to the ability to use sympy functions without any deeper understanding, these presentations are intended to give you insight into the origin of the series representation and the factors present within each term.  While the algebraic presentation isn't a general case, the essential elements of a general polynomial representation are visible.
The function $f(x)$ can be expanded into an infinite series or a finite series plus an error term.  Assume that the function has a continuous nth derivative over the interval $a \le x \le b$.  Integrate the nth derivative n times:
$$\int_a^x f^n(x) dx = f^{(n-1)}(x) - f^{(n-1)}(a)$$
The power on the function $f$ in the equation above indicates the order of the derivative.  Do this n times and then solve for f(x) to recover Taylor's series.  One of the key features in this derivation is that the integral is definite.  This derivation is outlined on Wolfram’s Mathworld.
As a second exercise, assume that we wish to expand sin(x) about x=0.  First, assume that the series exists and can be written as a power series with unknown coefficients.  As a first step, differentiate the series and the function we are expanding.  Next, let the value of x go to the value of the expansion point and it will be possible to evaluate the coefficients in turn:
$$
\sin x = A+Bx+Cx^2+Dx^3+Ex^4
$$
We can choose an expansion point (e.g. $x = 0$) and differentiate to get a set of simultaneous equations permitting determination of the coefficients.  The computer algebra system can help us with this activity:
Can you write Python code for it?

import sympy as sp
sp.init_printing()

x, A, B, C, D, E = sp.symbols('x, A, B, C, D, E')


I am working on the file "Lecture-10A-Taylors-Series.ipynb".
The first task is:
The function plot_taylor_approximations included here was written by Fernando Perez and was part of work on the original IPython project.  Although attribution seems to have been lost over time, we gratefully acknowledge FP and thank him for this code!
Can you write Python code for it?

def plot_taylor_approximations(func, x0=None, orders=(2, 4), xrange=(0,1), yrange=None, npts=200):
    """Plot the Taylor series approximations to a function at various orders.

    Parameters
    ----------
    func : a sympy function
    x0 : float
      Origin of the Taylor series expansion.  If not given, x0=xrange[0].
    orders : list
      List of integers with the orders of Taylor series to show.  Default is (2, 4).
    xrange : 2-tuple or array.
      Either an (xmin, xmax) tuple indicating the x range for the plot (default is (0, 1)),
      or the actual array of values to use.
    yrange : 2-tuple
      (ymin, ymax) tuple indicating the y range for the plot.  If not given,
      the full range of values will be automatically used.
    npts : int
      Number of points to sample the x range with.  Default is 200.
    """
    if not callable(func):
        raise ValueError('func must be callable')
    if isinstance(xrange, (list, tuple)):
        x = np.linspace(float(xrange[0]), float(xrange[1]), npts)
    else:
        x = xrange
    if x0 is None: x0 = x[0]
    xs = sp.Symbol('x')
    # Make a numpy-callable form of the original function for plotting
    fx = func(xs)
    f = sp.lambdify(xs, fx, modules=['numpy'])
    # We could use latex(fx) instead of str(), but matploblib gets confused
    # with some of the (valid) latex constructs sympy emits.  So we play it safe.
    plt.plot(x, f(x), label=str(fx), lw=2)
    # Build the Taylor approximations, plotting as we go
    apps = {}
    for order in orders:
        app = fx.series(xs, x0, n=order).removeO()
        apps[order] = app
        # Must be careful here: if the approximation is a constant, we can't
        # blindly use lambdify as it won't do the right thing.  In that case,
        # evaluate the number as a float and fill the y array with that value.
        if isinstance(app, sp.numbers.Number):
            y = np.zeros_like(x)
            y.fill(app.evalf())
        else:
            fa = sp.lambdify(xs, app, modules=['numpy'])
     ...


I am working on the file "Lecture-10A-Taylors-Series.ipynb".
The first task is:
Your markdown here.
Computing a Taylor's Series Symbolically

Using sympy the Taylor's series can be computed symbolically.
Can you write Python code for it?

from sympy import init_printing, symbols, Function
init_printing()

x, c = symbols("x,c")
f = Function("f")

f(x).series(x, x0=c, n=3)


I am working on the file "Lecture-10B-Reading-and-Writing-Data.ipynb".
The first task is:
Note the use of np.linspace to give you uniform grid spacing for your data points.
Making two dimensional data is a bit more involved.  In the case below we create a set of $(x,y)$ pairs and then use np.meshgrid to facilitate vectorizing the calculation of the value stored in z3.  For this function we use the discrete values of X and Y to compute the quantity:
$$\sin(X) \cos(Y)^2$$
We store this in z3 and write to a file.
Can you write Python code for it?

x3 = np.linspace(0, 10, 100)
y3 = np.linspace(0, 10, 100)
X, Y = np.meshgrid(x3, y3)
z3 = np.sin(X)**2*np.cos(Y)**2
np.savetxt('test_data_003.txt', (z3))


I am working on the file "Lecture-10B-Reading-and-Writing-Data.ipynb".
The first task is:
The benefit in using the np.savetxt function is that you can use the inverse np.loadtxt easily.  Here we load the test data file back into a variable testZ and pass that array to an image.
Can you write Python code for it?

%matplotlib notebook

import matplotlib.pyplot as plt
from matplotlib import cm

testZ = np.loadtxt('test_data_003.txt')

# Make plot with vertical (default) colorbar
fig, ax = plt.subplots()
cax = ax.imshow(testZ, interpolation='nearest', cmap=cm.coolwarm)
ax.set_title('Scalar Field')

plt.show()


I am working on the file "Lecture-11-Scalars-Vectors.ipynb".
The first task is:
Divergence

If we interpret the gradient field as a measurement of the flow of some quantity through space, then the divergence of that vector field is a measurement of the accumulation of that quantity.  To compute the divergence we compute the dot product of nabla and the vector field.  The resulting quantity is a scalar quantity:
$$
\overrightarrow{\nabla} \cdot \overrightarrow{F} = \frac{\partial F_x}{\partial x} + \frac{\partial F_y}{\partial y} + \frac{\partial F_z}{\partial z}
$$
Accumulation and Diffusion

One of the key problems in science and engineering is the diffusion of an extensive quantity such as energy, charge, or mass.  The gradient of a potential provides a driving force for this diffusion.
Using the diffusion of mass as an example:
$$\overrightarrow{J} = -M \overrightarrow{\nabla} \cdot {\mu} $$
and then computing the accumulation based on the vector field:
$$\frac{\partial X(x,t)}{\partial t} = - \overrightarrow{\nabla} \cdot \overrightarrow{J} $$
where the minus sign indicates that accumulation occurs antiparallel to the gradient.  You may have seen
Plotting the Flux (or Accumulation)

In this section we keep track of the vector components by hand using the mathematical definitions.  Writing this out we have a scalar field:
$$
F(x,y)
$$
the flux written as minus the gradient of the field (with $M=1$):
$$
\overrightarrow{J} = -\nabla F(x,y) = -\left(\frac{\partial F}{\partial x} \hat{i} + \frac{\partial F}{\partial y}\hat{j} \right)
$$
and the accumulation as minus the divergence of the flux:
$$
A = -\nabla \cdot \overrightarrow{J} = \left(\frac{\partial^2 F}{\partial x^2} + \frac{\partial^2 F}{\partial y^2}\right)
$$
Can you write Python code for it?

x, y = sp.symbols('x y')

# Note NEW field function:
concentrationFunction = sp.sin(sp.pi*x)*sp.cos(sp.pi*y)

fluxX = -sp.diff(concentrationFunction,x)
fluxY = -sp.diff(concentrationFunction,y)
accumulationFunction = sp.diff(concentrationFunction,x,2) + sp.diff(concentrationFunction,y,2)

# We use lambdify to permit the functions to take arguments and vectorize the computations.
myConcentrationFunction = sp.lambdify((x,y), concentrationFunction, 'numpy')
myFluxX = sp.lambdify((x,y), fluxX, 'numpy')
myFluxY = sp.lambdify((x,y), fluxY, 'numpy')
myAccumulationFunction = sp.lambdify((x,y), accumulationFunction, 'numpy')

concentrationFunction

fluxX

fluxY


I am working on the file "Lecture-11-Scalars-Vectors.ipynb".
The first task is:
Gradients

Scalar fields have associated vector fields.  One such vector field is known as the gradient and is indicated with the symbol $\nabla$ and is called nabla.  The gradient is a type of directional derivative and is a vector quantity.  In a physical problem of three dimensions the gradient can be thought of as pointing in the direction of the maximum spatial rate of change.  Each basis vector magnitude is multiplied by the partial derivative of the field with respect to the basis vector's coordinate.
You can visualize the gradient operator as a vector with components:
$$\overrightarrow{\nabla} = \frac{\partial}{\partial x} \hat{i}  + \frac{\partial}{\partial y} \hat{j} + \frac{\partial}{\partial z} \hat{k}  $$
When applied to a scalar field the result is a vector field - the gradient.  Geometrically, the gradient is a vector field that "points uphill".  The following illustrations are meant to convey some of that geometric intuition.
Quiver Plots

One way to visualize a vector field in a physical problem is to use small oriented arrow to indicate direction and length to indicate magnitude.  These are known as quiver plots and they can be used in conjunction with heat maps to help visualize vector fields.  In the example below the scalar field is sampled with two different point densities.  A higher point density (100 points) for the filled contour plot and a lower point density (30 points) for the quivers.
Can you write Python code for it?

x0, x1 = (-3,3)
y0, y1 = (-2,2)

# Read the docstrings to understand why the numbers are given
# as complex quantities.  use: ?np.mgrid
Y, X =  np.mgrid[y0:y1:100j, x0:x1:100j]
Y1, X1 =  np.mgrid[y0:y1:25j, x0:x1:25j]

Z = np.sin(2*X)*np.sin(Y)

# u and v here are the results of applying the gradient operation
# to our scalar field.  Probably wise to check this in a seperate
# code block.
u = (2*np.sin(Y1)*np.cos(2*X1))
v = (np.sin(2*X1)*np.cos(Y1))

fig, ax = plt.subplots()
filled_contour = plt.contourf(X,Y,Z,5)
fig.colorbar(filled_contour, ax=ax)
plt.quiver(X1,Y1,u,v, color='white')
plt.show()


I am working on the file "Lecture-11-Scalars-Vectors.ipynb".
The first task is:
Visualizing the Scalar Field by Value

Color choice requires consideration of the medium in which you distribute your data as well as the capabilities of the reader.  An essay has been written on the topic and provides some guidance for preparation of figures.  matplotlib has a module called cm that provides some standard color maps.  User specified maps are possible.
DIY:  Using Surface and Contour Plots

Plot the electric field around a point charge if the potential is given by:
$$
V(x,y) = \frac{1}{x^2+y^2}
$$
Use three dimensional surface plots and contour plots to help visualize this potential.
Can you write Python code for it?


I am working on the file "Lecture-11-Scalars-Vectors.ipynb".
The first task is:
We then compute the divergence of the gradient.  Note the absence of C.i and C.j in the answer indicating that these are not components of a vector.  (Compare this to the last slide.)
Can you write Python code for it?

# Compute the divergence.
accumulation = -spv.divergence(gradientOfField)
accumulation


I am working on the file "Lecture-11-Scalars-Vectors.ipynb".
The first task is:
We then use the built in sympy function gradient to compute the gradient:
Can you write Python code for it?

# Compute the gradient.
gradientOfField = -spv.gradient(exampleField)
gradientOfField


I am working on the file "Lecture-11-Scalars-Vectors.ipynb".
The first task is:
We use our quiver plotting capability to plot:

the scalar value for the concentration as z
the $\hat{i}$ component of the flux as u
the $\hat{j}$ component of the flux as v
Can you write Python code for it?

import numpy as np

x0, x1 = (-1,1)
y0, y1 = (-1,1)
plotResolution = 200

Y, X =  np.mgrid[y0:y1:200j, x0:x1:200j]
# Quivers are on a seperate grid since they clutter things up.
Y1, X1 =  np.mgrid[y0:y1:20j, x0:x1:20j]

Z = myConcentrationFunction(X,Y)
u = myFluxX(X1,Y1)
v = myFluxY(X1,Y1)

fig, ax = plt.subplots()

from matplotlib import cm

plt.contourf(X,Y,Z,20, cmap=cm.coolwarm)
plt.colorbar()
plt.quiver(X1,Y1,u,v, color='white')

plt.show()

accumulationFunction


I am working on the file "Lecture-12-Fourier-Series.ipynb".
The first task is:
DIY:  Demonstrate the Inner Product of Certain Functions are Zero

Identify the conditions under which the inner product of:
$$
<\sin{4x}, \sin{x}>
$$
and
$$
<\sin{nx}, \sin{mx}>
$$
are zero.
The Fourier Series Definied on Arbitrary Ranges
This discussion is derived from Sean Mauch's open source Applied Mathematics textbook.  If $f(x)$ is defined over $c-L \leq x \leq c+L $ and $f(x+2L) = f(x)$ then $f(x)$ can be written as:
$$
f(x) = \frac{a_0}{2} + \sum_{n=1}^{\infty} a_n cos \left( \frac{n \pi (x+c)}{L} \right) + \sum_{m=1}^{\infty} b_m sin \left( \frac{m \pi (x+c)}{L} \right)
$$
and the coefficients:
$$ a_n = \langle f(x) | \cos \left( \frac{n\pi (x+c)}{L} \right) \rangle = \frac{1}{L}\int^{c+L}_{c-L} f(x) \cos \frac{n \pi (x+c)}{L} $$
and
$$ b_m = \langle f(x) | \sin \left( \frac{m\pi (x+c)}{L} \right) \rangle = \frac{1}{L}\int^{c+L}_{c-L} f(x) \sin \frac{m \pi (x+c)}{L} $$
Using our generalized dot product for functions as defined above we can compute the Fourier coefficients.  The code for this follows in functions a_n_amplitudes and b_m_amplitudes.
Computing the Fourier Coefficients by Hand

Note:  These next couple of cells take a few seconds to run.
Can you write Python code for it?

import sympy as sp
import numpy as np

x = sp.symbols('x')
dum = sp.symbols('dum')
sp.init_printing()

lam = 2
center = 1

def signal(x):
    return (x*(2 - x)*(1 - x)**2)

def mySpecialFunction(x):
    return sp.sin(2*x)

def b_m_amplitudes(n, funToProject, center, lam):
    return (2/lam)*sp.integrate(funToProject(dum)*sp.sin(2*n*sp.pi*dum/lam), (dum,center-lam/2,center+lam/2))

def a_n_amplitudes(m, funToProject, center, lam):
    return (2/lam)*sp.integrate(funToProject(dum)*sp.cos(2*m*sp.pi*dum/lam), (dum,center-lam/2,center+lam/2))

def b_m_vectorspace_element(n, var, lam):
    return sp.sin(2*n*sp.pi*var/lam)

def a_n_vectorspace_element(m, var, lam):
    if m==0:
        return sp.Rational(1,2)
    elif m!=0:
        return sp.cos(2*m*sp.pi*var/lam)

terms = 3
funToProject = signal

an_vectors = [a_n_vectorspace_element(n, x, lam) for n in range(terms)]
an_amplitudes = [a_n_amplitudes(n, funToProject, center, lam) for n in range(terms)]
bm_vectors = [b_m_vectorspace_element(m, x, lam) for m in range(terms)]
bm_amplitudes = [b_m_amplitudes(m, funToProject, center, lam) for m in range(terms)]


I am working on the file "Lecture-12-Fourier-Series.ipynb".
The first task is:
Using scipy we can perform this and other integrations numerically.  Two examples are given for the following functions:
Can you write Python code for it?

from scipy import integrate
import numpy as np

def myfunc1(x):
    return np.sin(4*x)

def myfunc2(x):
    return np.sin(x)

def myfunc3(x):
    return myfunc1(x)*myfunc2(x)

def myfuncx2(x):
    return x**2

[integrate.quad(myfuncx2, 0, 4), 4.0**3/3.0]

integrate.quad(myfunc3, 0, 2*np.pi)

import sympy as sp
sp.init_printing()

n, m = sp.symbols('n m', Integer=True)
x = sp.symbols('x')

def f(x):
    return sp.sin(n*x)

def g(x):
    return sp.sin(m*x)

# scope of variables in def is local.
def func_dot(f, g, lb, ub):
    return sp.integrate(f(x)*g(x), (x, lb, ub))

func_dot(f, g, 0, 2*sp.pi)


I am working on the file "Lecture-12-Fourier-Series.ipynb".
The first task is:
We can now plot this series and see the comparison of the signal (blue) and the series representation (red).  We can quantitatively describe the accuracy between the approximation and the function.
Can you write Python code for it?

p = sp.plot(signal(x), truncatedSeries, (x, 0, 2), show=False, title=r'Comparison of Series (Red) and Function (Blue)')
p[0].line_color = 'blue'
p[1].line_color = 'red'
p.show()


I am working on the file "Lecture-13-Integral-Transforms.ipynb".
The first task is:
Above we reprocess P into the image Im.
Can you write Python code for it?

fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(30,9))
axes[0].imshow(Z, origin='lower')
axes[1].imshow(abs(K),origin='lower', cmap=plt.get_cmap('pink'))
aperture1 = plt.Circle((128,128),3**2,color='r', fill = False)
aperture2 = plt.Circle((150,128),3**2,color='y', fill = False)
axes[1].add_artist(aperture1)
axes[1].add_artist(aperture2)
axes[2].imshow(abs(Im)**2, origin='lower')
plt.show()


I am working on the file "Lecture-13-Integral-Transforms.ipynb".
The first task is:
At some distance from the aperture we place a detector that measures the combined intensity of all the wave sources, however due to the finite width of the slit each wave travels a different distance to the detector.  The phase difference between the waves at the detector is given by the Fourier transform of the aperture function when the Fraunhofer approximation is valid.
This aperture function is even and real so we expect our transformed function to also be even and real.  We use the definition of the integral transform above to write an explicit integral statement of the Fourier transform of the top-hat function above.  The integral is $1$ between $c$ and $-c$ and zero elsewhere - so we can integrate just the non-zero part.  This is integrated as:
Can you write Python code for it?


I am working on the file "Lecture-13-Integral-Transforms.ipynb".
The first task is:
Calling explicitly for the integration and assigning the result to a:
Can you write Python code for it?

a = sp.sqrt(1/(2*sp.pi))*sp.integrate(1*sp.exp(-sp.I*2*omega*x),(x,-c,c))
a


I am working on the file "Lecture-13-Integral-Transforms.ipynb".
The first task is:
Diffraction from Two Apertures

We could perform the same integration over two top-hat functions and plot those results.
Can you write Python code for it?

compositeIntegral = sp.sqrt(1/(2*sp.pi))*sp.Integral(1*sp.exp(-sp.I*2*omega*x),(x,1,2)) + \
sp.sqrt(1/(2*sp.pi))*sp.Integral(1*sp.exp(-sp.I*2*omega*x),(x,-2,-1))
compositeIntegral

om = compositeIntegral.doit()
om


I am working on the file "Lecture-13-Integral-Transforms.ipynb".
The first task is:
Here we can use the subs (substitution) method to set the value of c.  I plotted the square of the function since the intensity of a diffracted wave is related to the time averaged energy transferred by the wave.  This is proportional to the amplitude squared.  As our function is real valued, we can take a shortcut and just plot the square.
Can you write Python code for it?


I am working on the file "Lecture-13-Integral-Transforms.ipynb".
The first task is:
I think it would be instructive to symbolically expand the matrix above so that it is clear how n*k leads to a two dimensional matrix.  Switching to sympy symbols to expose the details we can do the following:
Can you write Python code for it?

import sympy as sp
from sympy import Matrix
import numpy as np
sp.init_printing()


I am working on the file "Lecture-13-Integral-Transforms.ipynb".
The first task is:
In SymPy there are two ways to deal with integration.  If you would like to represent an unevaluated integral, you can use the Integral function.  If you want to compute the integration of an expression you can use the integrate function.
Can you write Python code for it?

sp.Integral(sp.E**(sp.I*omega*t),t)

# 'omega', positive=True
sp.integrate(sp.E**(sp.I*omega*t),t)


I am working on the file "Lecture-13-Integral-Transforms.ipynb".
The first task is:
Interactive Microscopy Demonstration (Optional)
Original developed by C. Carter, translated to Python by D. Lewis

Transmission electron microscopy utilizes diffraction to determine crystal structures and develop contrast in images.  In this section of the lecture we will simulate the diffraction pattern of an atomic structure.  Using this diffraction pattern we will simulate using a diffraction aperture to reconstruct a phase contrast image.
Can you write Python code for it?

%matplotlib inline
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from numpy.fft import *

def atomic_func(x,y):
    param = 64.0
    return (1+np.sin(4*(x+y)*2*np.pi/param))*(1+np.sin(2*(x-2*y)*2*np.pi/param))/4

def aperture(X, Y, xoffset, yoffset, size):
    return (X-xoffset)**2+(Y-yoffset)**2 > size**2


I am working on the file "Lecture-13-Integral-Transforms.ipynb".
The first task is:
Or we could functionalize our function to explore other parameters:
Can you write Python code for it?

def diffractionFunction(d=4.0, w=1.0):
    result = sp.sqrt(1/(2*sp.pi))*sp.Integral(1*sp.exp(-sp.I*2*omega*x),\
                                     (x,-(d+w),-(d-w))) + \
    sp.sqrt(1/(2*sp.pi))*sp.Integral(1*sp.exp(-sp.I*2*omega*x),\
                                     (x,(d-w),(d+w)))
    return result.doit()

sp.expand(diffractionFunction(10.,2.).rewrite(sp.sin))


I am working on the file "Lecture-13-Integral-Transforms.ipynb".
The first task is:
The P array holds the processed Fourier spectrum.  The values of P are set to zero when they are outside the aperture.  We use the K array to hold a opy of the image
In this cell we create two more numpy arrays (there are other ways to do this) that have the same shape as Z. The P array we use to hold the processed Fourier spectrum.  The processing uses numpy's Boolean indexing to set values in P equal to zero when they are "outside" the aperture.  When we get to the images below you'll see what is meant.
Because Python passes by reference we need to call for a copy of K so that we can modify one without changing the other.
From this processed spectrum we will create an image.  The K array holds the whole Fourier spectrum.
Can you write Python code for it?


I am working on the file "Lecture-13-Integral-Transforms.ipynb".
The first task is:
The Z array holds the atomic image function.
Can you write Python code for it?

P = np.zeros(Z.shape,dtype=complex)
K = np.zeros(Z.shape,dtype=complex)

K = fftshift(fft2(Z, norm='ortho'))

P = np.copy(K)
P[np.where(aperture(X, Y, 128, 128, 3) & aperture(X, Y, 150, 128, 3))] = 0


I am working on the file "Lecture-13-Integral-Transforms.ipynb".
The first task is:
The diffracted intensity from this pair of slits would appear as:
Can you write Python code for it?


I am working on the file "Lecture-13-Integral-Transforms.ipynb".
The first task is:
This does not (at first glance) appear to be a real function due to the two exponential terms, but we can use some of the algebraic methods built into SymPy to help.  We can ask for this form using sines and cosines with the rewrite method.  Furthermore - we can simplify it further with the expand function.  Trial and error may be required to determine the best combination and ordering of algebraic manipulations.
Can you write Python code for it?

solution = sp.expand(a.rewrite(sp.sin))
solution


I am working on the file "Lecture-13-Integral-Transforms.ipynb".
The first task is:
We can use the "all close" function to check if the result from DFT_slow and Numpy are close:
Can you write Python code for it?

x_signal = np.random.random(1024)
np.allclose(DFT_slow(x_signal), np.fft.fft(x_signal))


I am working on the file "Lecture-13-Integral-Transforms.ipynb".
The first task is:
We define two functions above:

atomic_func is used to provide an image function periodic in two dimensions from which the diffraction pattern will be constructed.  This can be thought of as the density of electrons in a solid that is used to approximate a crystal structure.
aperture returns a Boolean array that will be used to mask the diffraction pattern so that individual frequencies can be selected for image reconstruction.  aperture will return True or False.
Can you write Python code for it?

x = np.arange(0.0,256.0,1.0)
y = np.arange(0.0,256.0,1.0)
X,Y = np.meshgrid(x, y)

Z = atomic_func(X,Y)


I am working on the file "Lecture-13-Integral-Transforms.ipynb".
The first task is:
Where we assume there is no zero frequency (as we are dividing by $\omega$) - hence the assumption positive=True in the symbol definition above.  (Try replacing $\omega$ with $y$ and inspect the expression returned by integrate.)
The Fourier Transform

As the domain of the periodicity increases, the frequency spectrum required required to represent the function becomes more finely divided.  Recall the argument of the trigonometric terms in the functions of the Fourier series:
$$ \frac{n \pi (\omega +c)}{d} $$
where n is the order of the frequency component, c the offset relative to the origin, and d the domain width.  If we let the domain width go to infinity (implying that the function is not periodic) then an integral sum is required rather than a discrete summation.  The, infinte, non-periodic function and its frequency spectrum are related by the Fourier transform defined by:
$$ \hat{f}(\omega) = \sqrt{\frac{1}{2\pi}} \int^{+\infty}_{-\infty} f(t) \exp[-i \omega t] dt $$
This results in a mapping of the function f(t) into frequency space.
The real or complex and even or odd nature of the function $f(t)$ determines if the transformed function is even, odd, real, or complex.  For the purposes of materials crystal structures in this lecture we will be using even and real functions.
Diffraction from An Aperture

A useful physical problem requiring use of the Fourier transform is diffraction.  In this problem we will use a top-hat function to represent the location of an infinity of wave sources from an aperture.  We use the sp.Piecewise function to generate a "tophat" function for the Fourier transform.
Can you write Python code for it?

%matplotlib inline
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = 8, 4

p = sp.Piecewise((0,x<-1),(1,x<1),(0,True))
sp.plot(p);


I am working on the file "Lecture-14-Ordinary-Differential-Equations.ipynb".
The first task is:
A Second Example First Order ODE: Skydiving
This example is taking from Arfken's book.  Example problem 7.2.1.  The model describes a falling skydiver under a parachute.  We are trying to find the velocity profile as a function of time as well as the terminal velocity.  The equation of motion is:
$$ m \dot{v} = m g - b v^2 $$
where v is $v(t)$, a function of time.  Later in the problem we will provide values for $m$, $g$, and $b$ to find a particular solution.
Can you write Python code for it?

#from sympy import *
#init_session()

mass, g, b, t = symbols('mass g b t', real=True)
v = symbols('v', cls=Function)

paraChute = mass*v(t).diff(t)-mass*g+b*(v(t))**2
paraChute

solution = dsolve(paraChute,v(t))
solution

particularSolution = solution.subs([(v(t),0),(t,0)])
particularSolution

C1 = symbols('C1')

const = solve(particularSolution, C1)
const


I am working on the file "Lecture-14-Ordinary-Differential-Equations.ipynb".
The first task is:
First we will work out the solution for a constant applied stress, $\sigma_0$.  We start with the general solution and make the substitution that $\sigma(t)$ is constant by changing the symbol from a function of t to $\sigma_0$.
Can you write Python code for it?

generalSolution = dsolve(maxwellModel,epsilon(t))
generalSolution

constantStressSolution = dsolve(maxwellModel,epsilon(t)).subs(sigma(t),sigma0).doit()
constantStressSolution


I am working on the file "Lecture-14-Ordinary-Differential-Equations.ipynb".
The first task is:
Functionalize and plot the results.
Can you write Python code for it?

from ipywidgets import *

def plotSolution(M0, eta0):
    plot(solutionToPlot.subs([(M,M0),(eta,eta0),(sigma0,100)]),
         (t,0,5),
         ylim=(0,100),
         xlabel='time',
         ylabel=r'$\sigma(t)$'
        )

interact(plotSolution,M0=(1,100,1),eta0=(1,100,1));


I am working on the file "Lecture-14-Ordinary-Differential-Equations.ipynb".
The first task is:
Important ODE Two
This is the other ODE of interest:
$$
\frac{d^2 x(t)}{dt^2} \frac{1}{x(t)} + k = 0
$$
Solve this ODE with boundary conditions:
$$
x(t) = 0
$$
at $t = 0$ and $t = 2\pi$
Can you write Python code for it?

%matplotlib notebook
import sympy as sp
import numpy as np
from ipywidgets import interact, fixed
sp.init_session()
sp.init_printing()

t = sp.symbols('t')
x = sp.symbols('x', cls=Function)
k, alpha = sp.symbols('k alpha', positive=True)

secondModelToSolve = x(t).diff(t,2)/x(t) + k**2
sp.var('C1 C2')
solutionToSecondModel = sp.dsolve(secondModelToSolve).subs(C2,0)
solutionToSecondModel


I am working on the file "Lecture-14-Ordinary-Differential-Equations.ipynb".
The first task is:
In this problem we will let stress be a constant for all time:
$$ \sigma(t) = \sigma_0 $$
Can you write Python code for it?


I am working on the file "Lecture-14-Ordinary-Differential-Equations.ipynb".
The first task is:
Now we can plot the solution with appropriate substitutions for the two parameters and one constant.  We extract the RHS and use subs and ipywidgets to interactively plot the solution.  Alternatively you could lambdafy the solution and use numpy to develop the interactive plot (you might be more satisfied with the results).
Can you write Python code for it?

def plotSolution(eta0,M0):
    plot(solutionToPlot.subs([(eta,eta0),(M,M0),(sigma0,100)]),
         (t,0,5),
         #ylim=(0,1000),
         xlabel='time',
         ylabel=r'$\epsilon(t)$'
        )

interact(plotSolution,eta0=(1,100,1),M0=(1,100,1));


I am working on the file "Lecture-14-Ordinary-Differential-Equations.ipynb".
The first task is:
Now we can substitute and plot the solution.
Can you write Python code for it?

#%matplotlib notebook

plot(final_solution.subs([(g,9.8),(mass,70000),(b,700000)]),(t,0,2))


I am working on the file "Lecture-14-Ordinary-Differential-Equations.ipynb".
The first task is:
Numpy and SciPy give us the ability to do this work numerically, too.
Can you write Python code for it?

#%matplotlib notebook

from scipy.integrate import odeint  # for integrate.odeint
from pylab import *  # for plotting commands

def deriv(velocity,time):
    mass = 70000.
    drag = 700000.
    gravity = 9.8
    dvdt = gravity-(drag/mass)*velocity**2
    return array(dvdt)

time = linspace(0.0,1.0,1000)
yinit = array([0.0])  # initial values
y = odeint(deriv,yinit,time)

figure()
plot(time, y[:,0])
xlabel('t')
ylabel('y')
show()


I am working on the file "Lecture-14-Ordinary-Differential-Equations.ipynb".
The first task is:
Part 2:  Review of Viscoelasticity

The spring obeys Hooke's law:
$$\sigma=M\epsilon $$
where $\sigma$ is the stress, $M$ is the modulus of elasticity in the linear regime, and $\epsilon$ is the elastic strain.
The dashpot obeys:
$$\sigma = \eta \frac{d \epsilon}{dt}$$
where $\sigma$, and $\epsilon$ are defined as above and $\eta$ is related to the viscous response.
Some materials exhibit time dependent behavior under loading.  In polymers this behavior originates from chain elasticity due to coiling and uncoiling and non-reversible motion of the chains relative to one another.  The elastic and viscous behaviors are typically modeled using the spring and dashpot.
In this lecture we will discuss the Maxwell model and the Kelvin model of viscoelastic materials.  Combinations of springs and dashpots can provide insight into materials behavior.  Combining many such Maxwell and Kelvin models can give insight into the spectrum of dynamic mechanical behavior seen in polymer systems.
Maxwell Model

Kelvin Model

To give you some concept for what we will discuss, this section covers a short derivation for the spring and dashpot in series.  Our first assumption is that for a spring and a dashpot in series that the stress is equally carried by each member in the load train and that the strain is a sum of strains in each element.  This is stated as:
$$ \epsilon_{total} = \epsilon_s + \epsilon_d $$
Taking the time derivative for each term produces:
$$ \frac{d\epsilon_{\mathrm{total}}}{dt} = \frac{d\epsilon_s}{dt} + \frac{d\epsilon_d}{dt} $$
We assume that the spring response is instantaneous so that we can write:
$$\frac{d\sigma}{dt} = M\frac{d\epsilon}{dt}  $$
Using the condition that the stresses are the same reduces the number of variables by one.  Using the condition that the strains add up to the total strain results in:
$$ \frac{d \epsilon_{total}(t)}{dt} = \frac{1}{M} \frac{d \sigma(t)}{dt} + \frac{\sigma(t)}{\eta}$$
Don't forget that both stress and strain are functions of time.
Models of Viscoelastic...
Can you write Python code for it?

%matplotlib notebook
from ipywidgets import interact, fixed
import sympy as sp

sp.init_session()
sp.var('omega sigma epsilon eta M sigma0 C1 C2')
sp.var('sigma epsilon', cls=Function)


I am working on the file "Lecture-14-Ordinary-Differential-Equations.ipynb".
The first task is:
The Maxwell model is defined as follows:
Can you write Python code for it?

maxwellModel = Eq(epsilon(t).diff(t), sigma(t).diff(t)/M + sigma(t)/eta)
maxwellModel


I am working on the file "Lecture-14-Ordinary-Differential-Equations.ipynb".
The first task is:
The major takeawy here is that strain rises linearly with time.
Models of Viscoelastic Behavior:  Maxwell Model - Constant Strain
Because the strain is constant the time rate of change is zero and we can make ths substitution right from the start.
Can you write Python code for it?

%matplotlib notebook
from ipywidgets import interact, fixed
import sympy as sp
sp.init_session()

sp.var('t, M, eta, epsilon0, sigma0, C1, C2')
sp.var('epsilon, sigma', cls=Function)


I am working on the file "Lecture-14-Ordinary-Differential-Equations.ipynb".
The first task is:
There are other ways to specify the derivative.
Can you write Python code for it?

dsolve(sp.diff(f(x),x,2) + 9*f(x), f(x))


I am working on the file "Lecture-14-Ordinary-Differential-Equations.ipynb".
The first task is:
This time with no substitutions.
Can you write Python code for it?

L, R, t, V0 = symbols('L R t V0', positive=True)
A, V = symbols('A V', cls=Function)

rLCircuit = A(t).diff(t) + (R/L)*A(t) - V(t)
rLCircuit

solution = dsolve(rLCircuit, A(t))
solution

particularSolution = dsolve(rLCircuit, A(t)).subs([(A(t),0),(t,0),(V(t),V0)]).doit()
particularSolution

const1 = -L*V0/R

simplify(solution.subs([(C1,const1),(V(t),V0)]).doit())


I am working on the file "Lecture-14-Ordinary-Differential-Equations.ipynb".
The first task is:
We assign to the variable equationToSolve the equation we are going to pass to dsolve().  At this point I have to say that Mathematica is really way more advanced than sympy.  If you find yourself really doing some heavy computing you may want to default to numerical methods if you are desperate to stay with Python or consider switching to Mathematica to do symbolic calculations.  I'm really caught between two worlds on this one.
Can you write Python code for it?

# This creates a relation between two objects.
?Eq

equationToSolve = Eq(2*current(t).diff(t) + 9*current(t), 100)
equationToSolve


I am working on the file "Lecture-14-Ordinary-Differential-Equations.ipynb".
The first task is:
We need to think a bit about our identities.
Can you write Python code for it?

1/tanh(t - I*pi/2).expand(complex=True)

1/tanh(t - I*pi/2).expand(complex=True).simplify()

final_solution = (solution.subs(C1,const[1]).rhs).expand().simplify()
final_solution


I am working on the file "Lecture-14-Ordinary-Differential-Equations.ipynb".
The first task is:
What does this say?  We apply an initial stress at time zero.  The material strains to whatever is predicted by Hooke's law.  And then, without changing the strain again through the experiment, the stress drops exponentially.
Can you write Python code for it?

solutionToPlot = solution.subs([(C1,sigma0)]).rhs
solutionToPlot


I am working on the file "Lecture-15-Separation-of-Variables.ipynb".
The first task is:
Invoke dsolve()
Can you write Python code for it?


I am working on the file "Lecture-15-Separation-of-Variables.ipynb".
The first task is:
So - wait, what?  Can can we let $C1=0$ too?
That IS a solution.  It just is not the one we're looking for, so we need to examine how the third factor (the sin term) can help us.
What about the exponential?  At the boundary there is no dependence of $x$ in this factor however we know that the parameters in the exponential will not cause that factor to go to zero.  It starts at one and just gets smaller - so looking at the second factor will not help us much.
This means that we have to let $\sin(\gamma)=0$.  We know this can happen with the right choice of $\gamma$.  What choices can we use?
What if:
$$\gamma = \pm \pi, \pm 2\pi, \pm 3\pi, ... $$
and in general:
$$\gamma = \pm n\pi$$
Let us go back an examine the initial condition where $t=0$.
Can you write Python code for it?


I am working on the file "Lecture-15-Separation-of-Variables.ipynb".
The first task is:
The solution (with constants) is the product of the two solutions above:
$$
c(x,t) = X(x)T(t) = \Big( C_{1} \sin{\left (\gamma x \right )} + C_{2} \cos{\left (\gamma x \right )} \Big)\Big ( C_{3} e^{- \alpha^{2} \gamma^{2} t} \Big )
$$
We will need to "absorb" the time solution constant ($C_3$) into the space solution constants.  There are an infinity of solutions depending on our choices of $C_1$ and $C_2$.  These will ultimately be determined by the boundary conditions.
I would also note here the following:  the CAS (SymPy in this case) did all the work for us above.  The code below is really to just keep ourselves organized.  I don't intend to do much with generalSolution except to make substitutions to find the boundary conditions.  Working with a CAS is equal parts setup, organization, and deciphering the results.
Clean up the solution for use below.  Eliminate one of the constants as the product of C1 and C2 is also a constant.  Sympy does not automatically create the symbols corresponding to the constants for us.  So we need to create those before proceeding:
Can you write Python code for it?

var('C1 C2')

generalSolution = Eq(c(x,t),(timePiece.rhs).subs([(C1,1)])*spacePiece.rhs)
generalSolution


I am working on the file "Lecture-15-Separation-of-Variables.ipynb".
The first task is:
Using the interactive widgets above, explore different combinations of parameters and connect your physical intuition for diffusion with the solution.  The orange line is the line $\phi(x,0)=x$ and the blue line is the solution to the diffusion equation.  As you advance time try and connect your physical intuition of diffusion with the evolution of the area under the blue curve in the solution.  At the boundaries the diffusing species is allowed to escape the medium.  Over time, we should see a gradual decrease in the amount of substance (shaded area under the blue curve) in the solution to the equation.
1D Diffusion Problem - Separating Variables

There are many ways to solve a PDE.  Separating variables is one such method to reduce a PDE to a pair (or more) of ODEs.  We start by assuming that the solution has the form of a time dependent piece and a spatially dependent piece.  So we could write:
$$ c(x,t) = X(x) T(t) $$
We substitute the assumed form of the solution into Fick's law:
$$
\frac{\partial c(x,t)}{\partial t} = D \frac{\partial^2 c(x,t)}{\partial x^2}
$$
this gives us:
$$ \frac{\partial X(x) T(t)}{\partial t} = D \frac{\partial^2 X(x) T(t)}{\partial x^2} $$
We will differentate the assumed solution of $c(x,t)$ above according to the derivatives in the differential equation.  It is usual to let D be $\alpha^2$ to guarantee that D is always positive, we can do this in sympy with a keyword in the symbols function call.
Can you write Python code for it?

import sympy as sp
sp.init_session(quiet=True)
sp.init_printing()

var('X,T, c',cls=Function)

var('alpha gamma', positive=True)


I am working on the file "Lecture-15-Separation-of-Variables.ipynb".
The first task is:
With our substitutions in place:
$$ \frac{\partial X(x) T(t)}{\partial t} = \alpha^2 \frac{\partial^2 X(x) T(t)}{\partial x^2} $$
After taking the derivatives we have:
$$ X(x)T'(t) = \alpha^2 X''(x)T(t) $$
We can arrange these expressions so that all the functions of "t" are on one side and the functions of "x" are on the other side.  Divide both the LHS and RHS of the above expression by $\alpha^2 X(x) T(t)$ to seperate variables on either sides of the equals sign.  Note also that this means that the product $\alpha^2 X(x) T(t)$ should never be zero.  Add a seperation constant $\gamma$ and integrate.
The rationalization for the seperation constant is that since the LHS and RHS each depend on one variable then the only way the LHS and RHS can be equal is if they are both equal to a constant.  Our ODEs are then written as:
Can you write Python code for it?

timeODE = sp.Eq(T(t).diff(t,1)/(alpha**2*T(t)),-gamma**2)
timeODE

spaceODE = sp.Eq(X(x).diff(x,2)/X(x),-gamma**2)
spaceODE


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
A quick reminder of the differential equation of interest:
$$
\left. s \,\, f(x) - f(x) \right|_{t=0} = D \frac{\partial^2 f(x)}{\partial x^2}
$$
Can you write Python code for it?


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
Calling sp.dsolve to find the function $f(x)$ we get:
Can you write Python code for it?

solutionToEquation = sp.dsolve(equationToSolve,f(x))
solutionToEquation


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
Consider the solution and what the values of the constants must be given the physical constraints on the problem.  Reason out the value of one of the constants and substitute.
Can you write Python code for it?

solutionXPositive = solutionToEquation # What substitution do you have to make?
solutionXPositive


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
Do the integration:
Can you write Python code for it?

massConstraintEvaluated = massConstraintIntegral.doit()
massConstraintEvaluated


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
Here is the Laplace transform of $\sin(\omega t)$:
Can you write Python code for it?

laplaceSinIntegral = sp.Integral(sp.sin(omega*t)*sp.exp(-s*t), (t,0,oo))
laplaceSinIntegral

laplaceSinIntegral.doit()


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
Here we set up a few symbols so that our notation is consistent.  It is imperitive that we place conditions on these parameters.  You can relax these conditions, but in most introductory textbooks these conditions are spelled out in the text rather than symbolically.  So it can be easy to miss the conditions if you are scanning the equations.
Can you write Python code for it?

s = symbols('s', positive=True)
omega = symbols('omega', positive=True)
C0 = symbols('C0', positive=True)
init_printing()


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
Identifying that $C_1 = C_0/s$.  Making the final substitutions:
Can you write Python code for it?

thingToInverseTransform = solutionToEquation.subs([(C2,0),(C1,C0/s)])
thingToInverseTransform.rhs


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
In Class - Part I

The Laplace Transform of Derivatives
This is where the strength of the method is realized.  We will use the product rule to develop an expression for the Laplace transform of a derivative with respect to time and space.  In time the transform requires integration by parts.  In space we require differentiation under the integral.
Start with our symbolic representation of a derivative quantity
Can you write Python code for it?


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
In the cell below - put your value for $C_1$ as determined above.
Can you write Python code for it?

finalResults = solutionXPositive.subs(C1, None)
finalResults


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
Invert the transformation.
Can you write Python code for it?


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
Lecture 16:  Analytical Solutions II:  The Laplace Transform and Semi Infinite Media
Sections

Introduction
Learning Goals
On Your Own
The Laplace Transform
The Laplace Transform of Derivatives

In Class
Solving a PDE using Laplace Transformations
Applying the Boundary Conditions

Homework
Summary
Looking Ahead
Reading Assignments and Practice

Introduction

While using the words "hard" or "not hard" are a bit unfair when discussing solutions to the diffusion equation, I will say that my personal experience is that problems with finite bounds are a bit trickier to set up than problems where the boundaries are located infinitely far away.  The simpler solutions to infinite/semi-infinite problems make them tempting to use on problems with small dimensions.  So that is a source of tension for the materials scientist!  Do I use the quick and easy solution, or do I try and simulate/compute the more-precise solution?  Understanding the limitations and quantifying the error (and, if possible bounding the error) is a useful tactic.
Set up our notebook with a few (usual) imports:
Can you write Python code for it?

%matplotlib inline
import numpy as np
import math as math
from scipy.special import erfc
from ipywidgets import interact, fixed
import matplotlib.pyplot as plt

# run this cell before class.

def diffusionSolution(x, t, C0, D):
    return C0*erfc(x/(2.0*np.sqrt(D*t)))

def myfig(t=0.1, C0=5.0, D=1.0):
    """
    This function plots a solution to the diffusion equation
    based on a Laplace transform solution.  Four inputs are
    required.

    """
    x = np.linspace(0.0, 100.0, 5000)
    y1 = diffusionSolution(x, t, C0, D)
    fig = plt.figure(figsize=(5,4))
    axes = fig.add_axes([0.1, 0.1, 0.8, 0.8])
    axes.plot(x, y1, 'r', label=r"$c(x,t)$")
    plt.fill_between(x, 0, y1, alpha=0.1, color='red')
    axes.set_xlim([0.0,10.0])
    axes.set_ylim([0.0,10.0])
    axes.legend()
    axes.grid(False)
    return

interact(myfig, t=(0.001,100.0,0.1), C0=(1.0,10.0,1.0), D=(1.0,10.0,1.0));


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
Let us write a definite integral for our mass constraint
$$
\int^\infty_0 c(x,t) = \frac{M}{2}
$$
Can you write Python code for it?

massConstraint = sp.Eq(sp.Integral(c(x,t),(x,0,sp.oo)),M/2)
massConstraint


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
Making our whole definition:
$$
\mathcal{L}(f_t) = \int_{0}^{\infty} e^{- s t} \frac{\partial f(x,t)}{\partial t}\, dt = [e^{-st}f(t)] \, \Big |^{\,t=\infty}{\, t=0} + \int{0}^{\infty} s f{\left (x,t \right )} e^{- s t}\, dt
$$
Where the $t$ subscript refers to differentiation in time.  It's that simple.  Some texts will continue from this point and define, generally, the Laplace transform of an n'th derivative.  The only remaining wrinkle is to find the Laplace transform of a spatial derivative and then we have all the tools we need to solve the diffusion equation using Laplace transforms.
Top of Page
To find the Laplace transform of the derivative of $f$ with respect to $x$, let us start with sympy:
Can you write Python code for it?


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
Now we invoke dsolve.
Can you write Python code for it?


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
OK.  So - we need to dive a bit deeper on how to define things here.  The quick answer is that you can change the order of operations.
The longer answer is that the operations for differentiation under the integral commute under certain conditions.  The previous link and this one provide some needed background. We've encountered this idea before when we define cross derivatives under partial differentiation:
$$
\frac{\partial}{\partial y} \left( \frac{\partial f}{\partial x} \right) \equiv \frac{\partial^2 f}{\partial y \partial x} = \frac{\partial^2 f}{\partial x \partial y}
$$
For what it's worth - I scanned my calculus texbook and an applied math text and neither text indexes this rule for easy reference.  I'm not sure how you would learn this on your own - except to rationalize that integration and differentiation are limit processes and if the variables of the limits are independent then maybe the operations commute.  The diffusion text (from which I pulled this discussion) does not provide reference or proof that the limit operations (i.e. differentation and integration) can be interchanged - the text merely says that it can be done.
$$
\mathcal{L}\left( \frac{\partial^2 f(x,t)}{\partial x^2} \right) = \frac{\partial^2}{\partial x^2} \mathcal{L} (f(x,t)) = \frac{\partial^2 A(x)}{\partial x^2}
$$
Ultimately, once you use the Laplace transform on the diffusion equation:
$$
D \frac{\partial^2 c}{\partial x^2} = \frac{\partial c}{\partial t}
$$
you end up with an ordinary differential equation in x alone.  The time dependence is removed by the integral.  A is the tranformed function:
$$
D \frac{\partial^2 A(x)}{\partial x^2} = s A(x)
$$
Top of Page
In Class - Part II

Solving a Differential Equation Using Laplace Transforms
Solving this differential equation is as simple as calling DSolve and investigating the behavior at the boundaries.  Don't forget to transform the boundary conditions, too.  The notation "A" here is used to remind you that after the Laplace transform we are in a different space.
Can you write Python code for it?

import sympy as sp
sp.init_printing()

f = sp.symbols('f', cls=sp.Function)
x = sp.symbols('x', real=True)
s = sp.symbols('s', real=True, positive=True)
C0, D = sp.symbols('C0 D', real=True, positive=True)
C1, C2 = sp.symbols('C1 C2')
t = sp.symbols('t', real=True, positive=True)


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
Our final solution is then:
$$
c(x,t) = \frac{M}{2\sqrt{\pi D t}} \exp \left(- \frac{x^{2}}{4 D t} \right)
$$
Top of Page
DIY:  Use interact to create a visualization of the above solution.
Can you write Python code for it?


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
Question:  All those times we write $x \propto \sqrt{DT}$ what are we really saying?  Did playing with the values above give you some insight?
Diffusion from a Point Source Located at the Origin

You won't have the above figure, but you should be able to make your own interact visualization to make one of your own.
Recall that the transform of:
$$
\frac{\partial c(x,t)}{\partial t} = D \frac{\partial^2 c(x,t)}{\partial x^2}
$$
is:
$$
\left. s \,\, f(x) - f(x) \right|_{t=0} = D \frac{\partial^2 f(x)}{\partial x^2}
$$
where $f$ is the Laplace transform of $c$.
Now we consider a point source located at the origin of our coordinate system.  Note that $c(x,0) = 0$ everywhere EXCEPT at $x=0$ where we have an amount of substance $M$.
In this problem we are solving over all space.  Recalling our general solution to this problem in Laplace space:
$$
f{\left (x \right )} = C_{1} e^{- \frac{\sqrt{s} x}{\sqrt{D}}} + C_{2} e^{\frac{\sqrt{s} x}{\sqrt{D}}}
$$
Let us examine the behavior of $C_1$ and $C_2$.  As $x \rightarrow \infty$, $C_2$ must be zero and as $x \rightarrow -\infty$, $C_1$ must go to zero to ensure that the solution remains finite.
So we can use the symmetry of the problem to our advantage to help us arrive at a solution.
The mass that will diffuse into the $x>0$ part of the system will be $M/2$ so we can define the equation we want to solve, and then Laplace transform the LHS boundary condition.
Can you write Python code for it?

# some initial imports.

import sympy as sp
sp.init_printing()

f, c = sp.symbols('f c', cls=sp.Function)
x = sp.symbols('x', real=True)
s = sp.symbols('s', real=True, positive=True)
C0, D, M = sp.symbols('C0 D M', real=True, positive=True)
C1, C2 = sp.symbols('C1 C2')
t = sp.symbols('t', real=True, positive=True)


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
Top of Page
Learning Goals

Practice computing integral transforms and their inverses using Python
Identify useful resources in textbooks or on the Web
Use the Laplace transform to solve a semi-infinite diffusion problem
Gain confidence to apply the Laplace transform in future lectures

Top of Page
On Your Own

The Laplace Transform
The Laplace transform is an integral transform.  The Laplace transform of a function $f(t)$ is defined to be:
$$
\int_0^{\infty} f(t) e^{-st} dt
$$
We will explore sympy's functionality for computing the Laplace transform.  But first let us to the integration explicitly.
Can you write Python code for it?

%matplotlib notebook

import sympy as sp
sp.init_session(quiet=True)


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
We can write the integral of the product (sympy will not evaluate this for us because of the use of Integral). We can call for integration with the doit() method:
Can you write Python code for it?

leftHandSide = sp.Integral(sp.Derivative(f(x,t)*sp.exp(-s*t),t),(t,0,oo))
leftHandSide


I am working on the file "Lecture-16-Laplace-Transforms.ipynb".
The first task is:
You can use the rhs and lhs attributes to get the bits of the functions you are interested in.
Can you write Python code for it?


I am working on the file "Lecture-17-Explicit-Finite-Difference.ipynb".
The first task is:
DIY:  Compute a solution where you change the boundary conditions on the LHS to be $c(x=L,t) = 1.0$.
Can you write Python code for it?


I am working on the file "Lecture-17-Explicit-Finite-Difference.ipynb".
The first task is:
DIY:  Sketch the algorithm up to this point and for the cell below.

Doing this will help you visualize the operations and it will increase your ability to make modifications in the future and devise new more compact ways to integrate this PDE.
If you've sketched the algorithm as advised above then you see that in our development of this solution we implicitly set the boundary conditions.  We initialize arrayWithAllTheData with np.zeros and then compute on all the interior rows/columns.  This creates a condition where all the boundary cells are set to zero and their values remain untouched throughout the computation.
Plot the results
Can you write Python code for it?

%matplotlib inline
import numpy as np
from ipywidgets import interact, fixed
import matplotlib.pyplot as plt

def plotArray(xPoints, dataArray, rowID=0):
    """
    This function in conjunction with interact() permits
    inspection of the contents of an array, row by row.  This
    is useful for some small tasks such as examining the results
    of a PDE solution.
    """
    x = xPoints
    y = dataArray[:,rowID]
    fig = plt.figure(figsize=(7,4))
    axes = fig.add_axes([0.1, 0.1, 0.8, 0.8])
    axes.set_ylim(0,1)
    axes.plot(x, y, 'ro', label=r"$c(x,t)$")
    axes.legend()
    axes.grid(False)
    plt.show()
    return

interact(plotArray,
         xPoints=fixed(xPoints),
         dataArray=fixed(arrayWithAllTheData),
         rowID=(0,numberOfIterations-1,1), );


I am working on the file "Lecture-17-Explicit-Finite-Difference.ipynb".
The first task is:
DIY:  Vectorize the above solution method.
Can you write Python code for it?


I am working on the file "Lecture-17-Explicit-Finite-Difference.ipynb".
The first task is:
You can think of the 2D array as having one space axis (the first index, we will use i for this one) and one time axis (the second index, we will use j for this one).
We will set our initial conditions by assigning the initialCondition array to the first row of the arrayWithAllTheData.  Note the slicing in the first index so that we can copy the contents of the initialCondition into the whole first row with a single assignment statement.
Can you write Python code for it?


I am working on the file "Lecture-18-Implicit-Finite-Difference.ipynb".
The first task is:
A simple function to plot the initial condition:
Can you write Python code for it?

def plotIC():
    fig = plt.figure()
    axes = fig.add_axes([0.1, 0.1, 0.8, 0.8])
    axes.plot(xPoints, initialCondition, 'ro')
    axes.set_xlabel('Distance $x$')
    axes.set_ylabel('Concentration of Stuff $c(x,t)$')
    axes.set_title('Initial Conditions');

plotIC()


I am working on the file "Lecture-18-Implicit-Finite-Difference.ipynb".
The first task is:
All that remains is to solve the above linear system.  Instead of using SymPy, we will use some tools in a different Python library.
Top of Page
An Implicit Numerical Solution
General setup in this section:
Can you write Python code for it?

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt


I am working on the file "Lecture-18-Implicit-Finite-Difference.ipynb".
The first task is:
First, some syntax:
Can you write Python code for it?


I am working on the file "Lecture-18-Implicit-Finite-Difference.ipynb".
The first task is:
Here is our vector of unknown quantities.  We know the solution to the $j-1$ time step.  All of these symbols represent the value of our field (e.g. concentration, temperature, etc.) at the $j$'th time step.
Can you write Python code for it?

xmatrix = sp.Matrix([u1,u2,u3,u4,u5,u6])
xmatrix


I am working on the file "Lecture-18-Implicit-Finite-Difference.ipynb".
The first task is:
If we've got everything correct, this matrix product will reproduce the discrete diffusion equation outlined above.  You'll note that the boundary equations are not formed correctly.  For reference, here is the discrete form:
$$
- \beta u_{i - 1,\, j} + (1 + 2 \beta) u_{i,\, j} - \beta u_{i + 1,\, j} = u_{i,\, j-1}
$$
Can you write Python code for it?


I am working on the file "Lecture-18-Implicit-Finite-Difference.ipynb".
The first task is:
It is worth noting that these schemes are unconditionally stable - so any choice of time step will produce a solution.  The accuracy of the solution does depend on this choice, though.
Can you write Python code for it?

diffusionCoefficient = 10.0
dt = dx**2/(diffusionCoefficient)
numberOfIterations = 1000


I am working on the file "Lecture-18-Implicit-Finite-Difference.ipynb".
The first task is:
Simulation parameters:
Can you write Python code for it?

numberOfPoints = 100
lengthOfDomain = 1.0
dx = lengthOfDomain/numberOfPoints
xPoints = np.linspace(0.0, lengthOfDomain, numberOfPoints)
initialCondition = np.sin(xPoints*np.pi/lengthOfDomain)


I am working on the file "Lecture-18-Implicit-Finite-Difference.ipynb".
The first task is:
Top of Page
Deconstruction of the Solution Scheme
In spite of the small chunk of code a few cells below, there is a lot going on.  Let us dissect it.  In bullet points:

Before the first solution step we enforce the boundary conditions.  Our choice of matrix means that we are using "fixed value" boundary conditions.  So we need to modify the b vector accordingly.  The indexing notation of Numpy that permits us to find the first ([0]) and last cell ([-1]) of an array is very helpful here.

```python
    oldConcentration[0] = oldConcentration[0] + uLHSdtdiffusionCoefficient/dx/dx
    oldConcentration[-1] = oldConcentration[-1] + uRHSdtdiffusionCoefficient/dx/dx
```
Recall:
$$
\left[\begin{matrix}2 \beta + 1 & - \beta & 0 & 0 & 0 & 0\- \beta & 2 \beta + 1 & - \beta & 0 & 0 & 0\0 & - \beta & 2 \beta + 1 & - \beta & 0 & 0\0 & 0 & - \beta & 2 \beta + 1 & - \beta & 0\0 & 0 & 0 & - \beta & 2 \beta + 1 & - \beta\0 & 0 & 0 & 0 & - \beta & 2 \beta + 1\end{matrix}\right] \cdot \left[\begin{matrix}u_{1}\u_{2}\u_{3}\u_{4}\u_{5}\u_{6}\end{matrix}\right] = \left[\begin{matrix}U_{LHS} \beta + b_{1}\b_{2}\b_{3}\b_{4}\b_{5}\U_{RHS} \beta + b_{6}\end{matrix}\right]
$$

Solving the system involves using the built in NumPy functions to invert the matrix.  What is returned is the solution vector.  Please note that I'm using an internal Numpy (an optimized function!) function to COPY the results of the linear algebra solution into the newConcentration vector.

python
    np.copyto(newConcentration,np.linalg.solve(A,oldConcentration))

Rather than storing ALL the data, we instead store just the current and the old concentrations.  There are efficiencies in doing this, but if we want the older values, we need to store them on disk or in memory.
Tuple unpacking in Python leads to the A,B=B,A syntax below.  This switches the references to the arrays.  This is important for efficiency - you don't want to move any data if you don't have to.  If you are running big calculations then moving that data around is a waste of time/resources. ...
Can you write Python code for it?

uLHS = 0.0
uRHS = 0.0
numIterations = 200

for i in range(numIterations):
    # enforce boundary conditions
    oldConcentration[0] = oldConcentration[0] + uLHS*dt*diffusionCoefficient/dx/dx
    oldConcentration[-1] = oldConcentration[-1] + uRHS*dt*diffusionCoefficient/dx/dx
    # solve the system
    np.copyto(newConcentration,np.linalg.solve(A,oldConcentration))
    # swap pointers
    oldConcentration, newConcentration = newConcentration, oldConcentration

# plot the results
fig2 = plt.figure()
axes = fig2.add_axes([0.1, 0.1, 0.8, 0.8])
axes.plot(xPoints, newConcentration, 'ro')
axes.set_ylim(0,1)
axes.set_xlabel('Distance $x$')
axes.set_ylabel('Concentration of Stuff $c(x,t)$')
axes.set_title('Solution');


I am working on the file "Lecture-18-Implicit-Finite-Difference.ipynb".
The first task is:
We create two solution vectors rather than one whole array to hold all of our solution.  This is not particular to the implicit method, but it demonstrates another technique for saving memory and speeding up the calculation.  We will fill these matrices and swap them (move data from new into old and overwrite new) at each time step.
Can you write Python code for it?

newConcentration = np.zeros((numberOfPoints), dtype='float32')
oldConcentration = np.zeros((numberOfPoints), dtype='float32')


I am working on the file "Lecture-18-Implicit-Finite-Difference.ipynb".
The first task is:
We define the symbols we want to use in our linear system.  For this demonstration, I don't add the time index but I keep my subscripts consistent with the figure above.
Can you write Python code for it?


I am working on the file "Lecture-18-Implicit-Finite-Difference.ipynb".
The first task is:
We first need to prime the arrays by copying the initial condition into oldConcentration.  Afterwards it will be enough to swap pointers (a variable that points to a memory location).
Can you write Python code for it?


I am working on the file "Lecture-19-Spectral-Solutions.ipynb".
The first task is:
After the computation, the concentration profile are displayed with a helper function:
Can you write Python code for it?

def makePlot():
    fig = plt.figure()
    axes = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # left, bottom, width, height (range 0 to 1)
    axes.plot(x, c_new.real, 'r')
    # Setting the y-limit cleans up the plot.
    axes.set_ylim([0.0,1.0])
    axes.set_xlabel('Distance $x$')
    axes.set_ylabel('Concentration $c(x,t)$')
    axes.set_title('Concentration Profile solved by Spectral Method')
    plt.show()
    return

makePlot()


I am working on the file "Lecture-19-Spectral-Solutions.ipynb".
The first task is:
Another solution scheme could preserve the values of the concentration.  I choose not to do that here for simplicity.  Doing so would permit a small animation of the diffusion process, however.  This is left to the student to implement.
Reading Assignments and Practice

Analyze the spectral difference schemes in the following two examples.  For reference and attribution the codes can be found here.  The materials are provided under a Creative Commons license with attribution to the original authors whose names can be found at the above link.
Can you write Python code for it?

# %load Heat_Eq_1D_Spectral_BE.py
#!/usr/bin/env python
"""
Solving Heat Equation using pseudospectral methods with Backwards Euler:
u_t= \alpha*u_xx
BC = u(0)=0 and u(2*pi)=0 (Periodic)
IC=sin(x)
"""

import math
import numpy
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator

# Grid
N = 64; h = 2*math.pi/N; x = [h*i for i in xrange(1,N+1)]

# Initial conditions
v = [math.sin(y) for y in x]
alpha = 0.5
t = 0
dt = .001 #Timestep size

# (ik)^2 Vector
I = complex(0,1)
k = numpy.array([I*n for n in range(0,N/2) + [0] + range(-N/2+1,0)])
k2=k**2;

# Setting up Plot
tmax = 5.0; tplot = 0.1
plotgap= int(round(tplot/dt))
nplots = int(round(tmax/tplot))
data = numpy.zeros((nplots+1,N))
data[0,:] = v
tdata = [t]

for i in xrange(nplots):
    v_hat = numpy.fft.fft(v)  # convert to fourier space
    for n in xrange(plotgap):
        v_hat = v_hat / (1-dt*alpha*k2) # backward Euler timestepping

    v = numpy.fft.ifft(v_hat)   # convert back to real space
    data[i+1,:] = numpy.real(v)   # records data

    t = t+plotgap*dt    # records real time
    tdata.append(t)

# Plot using mesh
xx,tt = (numpy.mat(A) for A in (numpy.meshgrid(x,tdata)))

fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(xx, tt, data,rstride=1, cstride=1, cmap=cm.jet,
        linewidth=0, antialiased=False)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.xlabel('x')
plt.ylabel('t')
plt.show()

# %load Heat_Eq_1D_Spectral_FE.py
#!/usr/bin/env python
"""
Solving Heat Equation using pseudo-spectral and Forward Euler
u_t= \alpha*u_xx
BC= u(0)=0, u(2*pi)=0
IC=sin(x)
"""

import math
import numpy
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator

# Grid
N = 64                     # Number of steps
h = 2*math.pi/N                 # step size
x = h*numpy.arange(0,N)    # discretize x-direction

alpha = 0.5                # Thermal Diffusivity constant
t...


I am working on the file "Lecture-19-Spectral-Solutions.ipynb".
The first task is:
If any the results are False then the numerical calculation will not converge, if they are all True then it is possible to complete the numerical calculation.  The next code block performs the numerical iterations of the amplitudes.  First the pointers to a_new and a_old are swapped and then a_new is filled with the new values based on the a_old values.  This sequence of operations is performed for the number of timesteps given in steps:
Can you write Python code for it?

for i in range(steps):
    # swap pointers
    a_new, a_old = a_old, a_new
    # find new amplitudes
    np.copyto(a_new, a_old*(1-(dt*np.pi**2*k2)/L**2))


I am working on the file "Lecture-19-Spectral-Solutions.ipynb".
The first task is:
Instabilities will occur if the amplitudes do not decay at each timestep.  The problem is that the condition depends on the wavenumber - so a suitable $\Delta t$ must be chosen that satisfies the most restrictive condition for the largest wavenumber.  Using a Boolean expression it is possible to check to see if all of the wavenumbers result in a factor less than one:
Can you write Python code for it?


I am working on the file "Lecture-19-Spectral-Solutions.ipynb".
The first task is:
The above solution results in the following difference scheme:
$$
a^{i+1}_k = a^{i}_k \left( 1 - \frac{\pi^{2} k^{2}}{L^{2}} \Delta t \right)
$$
The timestep, $dt$, should be chosen small enough such that the $a_k$ decay at each timestep.
Implementation of the Spectral Method

An annotated implementation of the spectral method is developed in the next section.  Unlike the development above, we use the full Fourier series for the basis functions.  This is a basic implementation that could be improved with the addition of helper and visualization functions.  SciPy provides $\sin$ and $\cos$ transforms for other boundary conditions where Fourier may be inappropriate.
Can you write Python code for it?


I am working on the file "Lecture-19-Spectral-Solutions.ipynb".
The first task is:
The following vectors are defined:

c_new will hold the $c(x,t)$ values at the start and end of the numerical computation
a_old will hold the amplitudes at the current timestep (i.e. the $a^{i}_k$)
a_new will hold the amplitudes at the next timestep (i.e. the $a^{i+1}_k$)

They are initalized to zero at the start of the calculation.  Each of these vectors is the same shape as x to hold the results of the FFT and IFFT.  They are declared as complex128 to accomodate different initial conditions where the FFT would produce complex valued results.
Can you write Python code for it?

c_new = np.zeros((numPoints), dtype='complex128')
a_old = np.zeros((numPoints), dtype='complex128')
a_new = np.zeros((numPoints), dtype='complex128')


I am working on the file "Lecture-20-Introduction-to-FiPy.ipynb".
The first task is:
Boundary conditions can be either fixed flux or fixed value.  Here, fixed value is used for simple comparison between the diffusion profiles.
Can you write Python code for it?


I am working on the file "Lecture-20-Introduction-to-FiPy.ipynb".
The first task is:
Concentration
Dependent Diffusion

Standard Imports
Can you write Python code for it?


I am working on the file "Lecture-20-Introduction-to-FiPy.ipynb".
The first task is:
Here we set the diffusion coefficient.
Can you write Python code for it?


I am working on the file "Lecture-20-Introduction-to-FiPy.ipynb".
The first task is:
Note:  The 'mesh' command creates the mesh (gridpoints) on which we will solve the equation.  This is specific to FiPy.
At this point, if you are in the IPython notebook I would suggest you try the following in the cell below:

Put your cursor to the right of the "(" and hit TAB.  You will get the docstring for the Grid1D function.
Do this again with your cursor to the right of the "d" in Grid1D().
And again after the "G".

This is a powerful way to explore the available functions.
Can you write Python code for it?


I am working on the file "Lecture-20-Introduction-to-FiPy.ipynb".
The first task is:
Note: In the first equation, the diffusion coefficient is constant, concentration independent.
Can you write Python code for it?


I am working on the file "Lecture-20-Introduction-to-FiPy.ipynb".
The first task is:
Note: In the second equation, the diffusion coefficient is non-constant and is a function of concentration in the system.  So we use D22_0 and D22_1 as the end points of our function. The function, given by "D" is simply a linear interpolation of the two D values.  You are, of course, free to try other functions.
Can you write Python code for it?


I am working on the file "Lecture-20-Introduction-to-FiPy.ipynb".
The first task is:
Setting Initial Conditions
Can you write Python code for it?

c.setValue(0.0)
viewer.plot()

c.setValue(0.2, where=x < L/2.)
c.setValue(0.8, where=x > L/2.)
viewer.plot()


I am working on the file "Lecture-20-Introduction-to-FiPy.ipynb".
The first task is:
These lines set diffusant in the initial condition.  They are set to the same values for easy comparison.  Feel free to change these values.
Can you write Python code for it?

c1.setValue(0.8)
c1.setValue(0.2, where=x > L/3.)
c2.setValue(0.8)
c2.setValue(0.2, where=x > L/3.)

viewer.plot()

viewer = Matplotlib1DViewer(vars = (c1,c2), limits = {'datamin': 0., 'datamax': 1.})
viewer.plot()


I am working on the file "Lecture-20-Introduction-to-FiPy.ipynb".
The first task is:
This line defines the diffusion equation:
Can you write Python code for it?

eqn1 = TransientTerm() == ImplicitDiffusionTerm(D11)

for step in range(1000):
    eqn1.solve(c, boundaryConditions = boundaryConditions, dt = timeStepDuration)
    viewer.plot()


I am working on the file "Lecture-20-Introduction-to-FiPy.ipynb".
The first task is:
We assign to 'c' objects that are 'CellVariables'.  This is a special type of variable used by FiPy to hold the values for the concentration in our solution.  We also create a viewer here so that we can inspect the values of 'c'.
Can you write Python code for it?

c = CellVariable(name = "c", mesh = mesh)
viewer = MatplotlibViewer(vars=(c,),datamin=-0.1, datamax=1.1, legend=None)


I am working on the file "Lecture-20-Introduction-to-FiPy.ipynb".
The first task is:
We assign to 'c' objects that are 'CellVariables'.  This is a special type of variable used by FiPy to hold the values for the concentration in our solution.  c1 for eqn1 and c2 for eqn2
Can you write Python code for it?

c1 = CellVariable(
    name = "c1",
    mesh = mesh,
    hasOld = True)

c2 = CellVariable(
    name = "c2",
    mesh = mesh,
    hasOld = True)


I am working on the file "Lecture-21-Calculus-of-Variations.ipynb".
The first task is:
A bit messy, but correct nonetheless.
DIY:  Find the Euler-Lagrange Equation (ELE)

Find the ELE for the functional, and if you can - solve for $y(x)$:
$$
v(y(x)) = \int_0^{\pi/2} (y_x^2 - y^2)dx
$$
The endpoint conditions are $y(0)=0$ and $y(\pi/2)=1$.  For reference, the general solution is:
$$
y(x)=C_1 \sin(x) + C_2 \cos(x)
$$
Don't forget to check the end points of the domain to find the constants.
The Problem of a Minimum Soapfilm

A classic problem in wetting and capillary science is that of the minimum soapfilm between two rings.  The soap film adopts a shape that minimizes its area.
The area of a soap film (found by rotating a curve through $2\pi$ around one axis is given by:
$$
A = L(x,y,y_x) = \int_{x_1}^{x_2} 2 \pi y (1+y_x^2)^{1/2} dx
$$
Note there is no explicit x dependence.
Can you write Python code for it?

Lsoapfilm = y(x)*sp.sqrt(1+(y(x).diff(x))**2)
(sp.euler_equations(Lsoapfilm,y(x),x)[0].lhs).simplify()


I am working on the file "Lecture-21-Calculus-of-Variations.ipynb".
The first task is:
And that is a linear function.  To the extent that this is a proof - you've proven that a straight line is the shortest distance between two points.
Using SymPy's Functions
SymPy has an euler_equation function that we can try to use, too:
Can you write Python code for it?

L = sp.sqrt(1+(y(x).diff(x))**2)
differentialEquationFromELFunction = sp.euler_equations(L, y(x), x)
differentialEquationFromELFunction


I am working on the file "Lecture-21-Calculus-of-Variations.ipynb".
The first task is:
Attacking the problem this way leads to a second order ODE that we need to integrate.  Although this could be done - the lack of an explicit $x$ dependence permits using an identity that makes the problem a bit easier.
An equivalent statement of the ELE is:
$$
\frac{d}{dx} \left(F - y_x \frac{\partial F}{\partial y_x} \right) = \frac{\partial F}{\partial x}
$$
If there is no explicit $x$ dependence therefore the RHS of the above equation is zero - the first integral can be had for "free". Adding the integration constant we have:
$$
F - y_x \frac{\partial F}{\partial y_x} = C_2
$$
We can therefore write:
Can you write Python code for it?

C2 = sp.symbols('C2', positive=True)
beltramiODE = sp.Eq(Lsoapfilm - y(x).diff(x)*Lsoapfilm.diff(y(x).diff(x)),C2)
beltramiODE.simplify()


I am working on the file "Lecture-21-Calculus-of-Variations.ipynb".
The first task is:
DIY:  Create a tool to explore the shape of different soapfilms.
Can you write Python code for it?

# Create an interactive widget to explore the values of the constants.


I am working on the file "Lecture-21-Calculus-of-Variations.ipynb".
The first task is:
DIY:  Use the general solution and find the constants.
Can you write Python code for it?

# Find the constants if the curve is required to pass through a pair of particular points.


I am working on the file "Lecture-21-Calculus-of-Variations.ipynb".
The first task is:
This clearly indicates that:
$$
\frac{dy}{dx} = C
$$
and from this point it should be clear that the function $y(x)$ and makes $F$ an extreme is:
$$
y = mx + b
$$
If you would like to have SymPy finish the calculation, you can write:
Can you write Python code for it?

functionalExtremizer = sp.dsolve(sp.Eq(y(x).diff(x),firstSolution[0]), y(x))
functionalExtremizer


I am working on the file "Lecture-21-Calculus-of-Variations.ipynb".
The first task is:
We define our functional.  Note that the functional is only a function of the derivative of $y$ rather than $y$ alone.
Can you write Python code for it?


I am working on the file "Lecture-21-Calculus-of-Variations.ipynb".
The first task is:
You may be tempted to do this:
Can you write Python code for it?


I am working on the file "Lecture-21-Calculus-of-Variations.ipynb".
The first task is:
You wouldn't be wrong to do the above, however things can be made a bit easier.  Let us look at each term in the ELE (Euler-Lagrange Equations):
Can you write Python code for it?

firstTerm = f.diff(y(x))
firstTerm

secondTerm = f.diff(y(x).diff(x))
secondTerm