Lab 7 ‐ Numerical Integration - MohamedS-Siena/computational_physics GitHub Wiki

Goal

We want to approximate the value of an integral of a function. To do so, we utilize the trapezoid integration method. The integral can also be calculated using the Monte Carlo method, and it provides a more visual depiction as well.

Overview

The trapezoidal integration method involves summing the trapezoidal area under a function for each $\Delta x$ between the range: $x_i$ to $x_f$. Each area is calculated via the following formula:

$$A = \frac{f(x_i)+f(x_i+\Delta x)}{2}\Delta x$$

We can create a function that utilizes this method, and we use it to calculate the area of a half-circle and the integral of the Gaussian distribution (Normal distribution). Later, we apply the Monte Carlo method to our half-circle example as an alternate way to determine its area. The Monte Carlo method involves generating random values for $x$ and $y$; those can then be plotted, and we can estimate the area based on the number of points that fall within the shape. This is given by:

$$\frac{N_{within}}{N_{total}}A_{total}$$

$A_{total}$ in this case refers to the total area of the plot.

This lab follows the following sequence:

  • Function for Trapezoid Integration Method
  • Area of Half-Circle
  • Integral of Gaussian Distribution
  • Compared to Built-In Functions
  • Monte Carlo

Function for Trapezoid Integration Method

Given arrays of x and y, we can define a function to calculate the area using the formula detailed above.

def trap(x,y):
  z=0
  for i in range(1,len(x)):
    z+=(y[i-1]+y[i])/2*(x[i]-x[i-1])
  return z

Area of Half-Circle

Given an array of x values, we define a function to calculate exact y values corresponding to a circle of specified radius.

def mycircle(x,r=2):
  y = np.sqrt(r**2-x**2)
  return y

To test our trapezoid function, we make an array for x for a number of values between -r and +r, and use the mycircle function to get an array for y.

x = np.linspace(-2,2,100)
y = mycircle(x)

a = trap(x,y)
print(f"Area of half of a circle of 2 radius: {a:.5f}")

This returns the value: 6.27644. This can be compared to the actual value: $2\pi$, which evaluates to about 6.283: about a 0.1% error between the two. If we increase the number of steps for x, the accuracy improves.

Gaussian Distribution

The Gaussian function is given by:

$$f(x) = \frac{1}{\sqrt{2\pi}\sigma}e^{\frac{1}{2}(\frac{x-\bar{x}}{\sigma})^2}$$

First, we define a function for this function:

def mygauss(x,mean,sigma):
  fx=1/(np.sqrt(2*np.pi)*sigma)*np.exp(-0.5*((x-mean)/sigma)**2)
  return fx

We can use our function alongside our trapezoid function to get the area and compare it to known values.

x = np.linspace(-1,1,1000)
y = mygauss(x,0,1)
a = trap(x,y)
a_theo = 0.682
error = (a-a_theo)/a_theo*100
print(f"percent error = {error}%")

This returns a %error of 0.101%.

x = np.linspace(-2,2,100)
y = mygauss(x,0,1)
a = trap(x,y)
a_theo = 0.272+0.682
error = (a-a_theo)/a_theo*100
print(f"percent error = {error}%")

This returns a %error of 0.049%.

x = np.linspace(-3,3,100)
y = mygauss(x,0,1)
a = trap(x,y)
a_theo = 0.042+0.272+0.682
error = (a-a_theo)/a_theo*100
print(f"percent error = {error}%")

This returns a %error of 0.130%.

Compared to Built-In Functions

Numpy has its own trapezoid integration function so we can compare our function to theirs.

# import scipy.integrate.trapezoid

# set up x and y arrays for the half circle
x = np.linspace(-2,2,100)
y = mycircle(x)
# call scipy.integrate.trapezoid to perform the integration
a=trap(x,y)
a_t=np.trapezoid(y,x)
# print your result and the expected answer in a formatted print statement
print(f"res: {a}, exp: {a_t}")

This returns: res: 6.276436078403621, exp: 6.276436078403619

# import scipy.integrate.trapezoid

# set up x and y arrays for the gaussian
x = np.linspace(-1,1,100)
y = mygauss(x,0,1)
# call scipy.integrate.trapezoid to perform the integration between $-1\sigma \le x \le 1\sigma$.
a=trap(x,y)
a_t=np.trapezoid(y,x)
# print your result and the expected answer in a formatted print statement
print(f"res: {a}, exp: {a_t}")

This returns: res: 0.6826730329991482, exp: 0.6826730329991479.

Monte Carlo

For x and y arrays containing random values within the plot area of our half-circle, we define a function to plot them and calculate the area.

def mcintegrate(x,y,visualize=False):
  if visualize==True:
    plt.figure()
    fx = mycircle(x)

    plt.xlabel("x")
    plt.ylabel("y")

    goodx=[]
    goody=[]
    badx=[]
    bady=[]

    for i in range(0,len(x)):
      if y[i]<=fx[i]:
        goodx.append(x[i])
        goody.append(y[i])
      else:
        badx.append(x[i])
        bady.append(y[i])

    plt.plot(goodx,goody,"bo",label="good")
    plt.plot(badx,bady,"ro",label="bad")



    title=len(goodx)/(len(goodx)+len(badx))*8
    plt.title(f"area={title}")

    x1=np.linspace(-2,2,100)
    y1=mycircle(x1)
    plt.plot(x1,y1,"k-",label="function",linewidth=4)

    plt.legend()
  else:
    return None

Then we run our function:

x=np.random.uniform(-2,2,1000)
y=np.random.uniform(0,2,1000)
mcintegrate(x,y,visualize=True)

Resultant plot:

image

The title of the plot (area) is calculated using the formula that was displayed above in the Overview.