Aerodynamics in Golf

A simulation of the effect of spin on dimpled and smooth golf balls.

Introduction

Using the equations for modelling drag, and lift of a golf ball along with measured values of a ball's aerodynamic profile, we can create simple models of the golf ball's flight.

Throughout this document, we have taken data from academic studies to assume coefficients of drag for different types of ball. We have also taken data for average ball speeds and rotation from a professional golf tracking company.

These sources are listed at the bottom of the document.

Simple model ignoring aerodynamic resistance

Let's start with a very simple model of projectile motion, ignoring all air resistance. First we need to set up the environment in python,

In [16]:
import numpy
import matplotlib.pyplot as pyplot

Create a function to model the flight of a golf ball, ignoring the effects of aerodynamic drag,

In [17]:
def simpleTrajectory(u, theta_deg):
    
    '''define the value for the acceleration due to gravity'''
    g = 9.81
    
    '''convert the angle to radians'''
    theta_rad = numpy.radians(theta_deg)
  
    '''use the angle to find the components of initial velocity'''
    u_x = u * numpy.cos(theta_rad)
    u_y = u * numpy.sin(theta_rad)
    
    '''find the time when the ball will hit the floor'''
    t_end = (2 * u_y) / g  
    
    '''create an array of points to sample'''
    t = numpy.linspace(0, t_end, 30)

    '''find the x, y, and z co-ordinates at each point'''
    s_x = u_x * t
    s_y = u_y * t - 0.5 * g * t ** 2
    
    '''return the output arrays of points'''
    return s_x, s_y
    

Plot the result of the model for a shot with initial velocty 40 m/s and launch angle 15 degrees.

In [26]:
initial_velocity = 40
launch_angle = 15

x_values = simpleTrajectory(initial_velocity, launch_angle)[0]
y_values = simpleTrajectory(initial_velocity, launch_angle)[1]


pyplot.plot(x_values, y_values, 'wo')
pyplot.xlim(0,100)
pyplot.ylim(0,20)
pyplot.gca().set_aspect('equal', adjustable='box')

pyplot.xlabel('Disance travelled (m)')
pyplot.ylabel('Height of the ball (m)')
pyplot.title('Simple trajectory model without drag')
pyplot.show()

print("The ball travelled ", x_values[-1], "m")
The ball travelled  81.5494393476 m

Simple model including drag

Drag in a situation is given by the equation: $$ F_{D}\,=\,{\tfrac {1}{2}}\,\rho \,v^{2}\,C_{D}\,A $$ where: $ F_{D}\ $ is the force exerted by drag, $ \rho $ is the density of the fluid in which the object is travelling, $ v $ is the velocity of the object, $ A $ is the cross-sectional area of the object, $ C_{D}\ $ is the coefficient of drag.

For a golf ball travelling through the air, we can assume the following values for $\rho$ and $ A $:

$\rho =$ 1.225 kg/m$^3$,

$ A = $ 1385.44 mm$^2$ or $=$0.001385m$^2$.

Finding the coefficient of drag is slightly more complex and involves analysing the ball's shape and surface texture. This is the variable that will change when we consider smooth and dimpled balls.

For the purpose of this simulation, we shall use a reference value for the values of the coefficient of drag for each the smooth and dimpled balls.

$C_{D} smooth = $ 0.45,

$C_{D} dimpled = $ 0.60.

In [27]:
def forceDrag(rho, v, C_D, area):
    
    force = - 0.5 * rho * v ** 2 * C_D * area
    
    return force

def dragTrajectory(u, theta_deg, C_D):
    
    '''initialise variables'''
    interval = 0.01
    g = - 9.81
    rho = 1.225
    area = 0.00138
    
    '''convert angles to radians'''
    theta_rad = numpy.radians(theta_deg)
    
    s_x = 0
    s_y = 0
    
    v_x = u * numpy.cos(theta_rad)
    v_y = u * numpy.sin(theta_rad)
    
    
    '''mass in kg'''
    m = 0.045
    
    a_x = forceDrag(rho, v_x, C_D, area)/m
    a_y = (forceDrag(rho, v_y, C_D, area) )/m +g

    
    '''intialise arrays'''
    x_array = numpy.array([0])
    y_array = numpy.array([0])
    t_array = numpy.array([0])
    
    count = 0 
    
    while s_y >= 0:
        
        count = count + 1
        time = count * interval
        
        '''find the next point's position '''
        s_x = s_x + v_x * interval + 0.5 * a_x * interval ** 2
        s_y = s_y + v_y * interval + 0.5 * a_y * interval ** 2
        
        '''find the new velocity at the next point'''
        v_x = v_x + a_x * interval
        v_y = v_y + a_y * interval
        
        '''find the new acceleration'''
        a_x = forceDrag(rho, v_x, C_D, area)/m
        a_y = (forceDrag(rho, v_y, C_D, area) )/m +g
        
        '''append data to arrays'''
        x_array = numpy.append(x_array, s_x)
        y_array = numpy.append(y_array, s_y)
        t_array = numpy.append(t_array, time)
        
    return x_array, y_array, t_array

Now, let's plot the path of the ball with drag against the one without drag.

In [32]:
initial_velocity = 40
launch_angle = 15
CD_smooth = 0.45
CD_dimpled = 0.2

x_values = simpleTrajectory(initial_velocity, launch_angle)[0]
y_values = simpleTrajectory(initial_velocity, launch_angle)[1]

x_values_drag_smooth = dragTrajectory(initial_velocity, launch_angle, CD_smooth)[0]
y_values_drag_smooth = dragTrajectory(initial_velocity, launch_angle, CD_smooth)[1]


x_values_drag_dimpled = dragTrajectory(initial_velocity, launch_angle, CD_dimpled)[0]
y_values_drag_dimpled = dragTrajectory(initial_velocity, launch_angle, CD_dimpled)[1]

pyplot.xlim(0,100)
pyplot.ylim(0,20)
pyplot.gca().set_aspect('equal', adjustable='box')
pyplot.plot(x_values, y_values, 'wo')
pyplot.plot(x_values_drag_smooth, y_values_drag_smooth, 'b')
pyplot.plot(x_values_drag_dimpled, y_values_drag_dimpled, 'g')

pyplot.xlabel('Distance travelled (m)')
pyplot.ylabel('Height of the ball (m)')
pyplot.title('Simple trajectory model with and without drag')
pyplot.show()

print("Distance travelled by ball with no drag :", x_values[-1], "m")
print("Distance travelled by smooth ball with drag :", x_values_drag_smooth[-1], "m")
print("Distance travelled by dimpled ball with drag :", x_values_drag_dimpled[-1], "m")
Distance travelled by ball with no drag : 81.5494393476 m
Distance travelled by smooth ball with drag : 60.5985898932 m
Distance travelled by dimpled ball with drag : 70.5145784608 m

Now with lift from rotation of the ball

The average golfer will usually hit the ball with a spin rate of around 3275 RPM. [Trackman Golf Tracking] Assuming this rate of spin, lets look at how lift due to the Magnus effect factors in.

In [21]:
def liftTrajectory(u, theta_deg, C_D, spin_RPM):
    
    '''initialise variables'''
    interval = 0.01
    g = - 9.81
    rho = 1.225
    area = 0.00138
    
    '''convert angles to radians'''
    theta_rad = numpy.radians(theta_deg)
    
    '''find the magnitude of the lift force'''
    lift_mag = 0.285 * (1-numpy.exp(-0.00026*spin_RPM))
    
    
    
    s_x = 0
    s_y = 0
    
    v_x = u * numpy.cos(theta_rad)
    v_y = u * numpy.sin(theta_rad)
    
    
    '''mass in kg'''
    m = 0.045
    
    a_x = forceDrag(rho, v_x, C_D, area)/m
    a_y = (forceDrag(rho, v_y, C_D, area) )/m +g 

    
    '''intialise arrays'''
    x_array = numpy.array([0])
    y_array = numpy.array([0])
    t_array = numpy.array([0])
    
    count = 0 
    
    while s_y >= 0:
        
        count = count + 1
        time = count * interval
        
        '''find the next point's position '''
        s_x = s_x + v_x * interval + 0.5 * a_x * interval ** 2
        s_y = s_y + v_y * interval + 0.5 * a_y * interval ** 2
        
        '''find the new velocity at the next point'''
        v_x = v_x + a_x * interval
        v_y = v_y + a_y * interval
        
        '''find the components of lift'''
        theta_i = numpy.arctan(v_y/v_x)
        l_x = lift_mag * numpy.sin(theta_i)
        l_y = lift_mag * numpy.cos(theta_i)
        
        '''find the new acceleration'''
        a_x = forceDrag(rho, v_x, C_D, area)/m + l_x/m
        a_y = (forceDrag(rho, v_y, C_D, area) )/m +g +l_y/m
        
        '''append data to arrays'''
        x_array = numpy.append(x_array, s_x)
        y_array = numpy.append(y_array, s_y)
        t_array = numpy.append(t_array, time)
        
    return x_array, y_array, t_array
In [25]:
initial_velocity = 40
launch_angle = 15
spin = 3275
CD_dimpled = 0.2

x_values = simpleTrajectory(initial_velocity, launch_angle)[0]
y_values = simpleTrajectory(initial_velocity, launch_angle)[1]


x_values_drag_dimpled = dragTrajectory(initial_velocity, launch_angle, CD_dimpled)[0]
y_values_drag_dimpled = dragTrajectory(initial_velocity, launch_angle, CD_dimpled)[1]

x_values_lift_dimpled = liftTrajectory(initial_velocity, launch_angle, CD_dimpled, spin)[0]
y_values_lift_dimpled = liftTrajectory(initial_velocity, launch_angle, CD_dimpled, spin)[1]

pyplot.xlim(0,120)
pyplot.ylim(0,15)
pyplot.gca().set_aspect('equal', adjustable='box')
pyplot.plot(x_values, y_values, 'wo')
pyplot.plot(x_values_lift_dimpled, y_values_lift_dimpled, 'b')
pyplot.plot(x_values_drag_dimpled, y_values_drag_dimpled, 'g')

pyplot.xlabel('Distance travelled (m)')
pyplot.ylabel('Height of the ball (m)')
pyplot.title('Simple trajectory model with and without drag')
pyplot.show()

print("Distance travelled by ball with no drag :", x_values[-1], "m")
print("Distance travelled by dimpled ball with drag :", x_values_drag_dimpled[-1], "m")
print("Distance travelled by dimpled ball with drag and lift :", x_values_lift_dimpled[-1], "m")
Distance travelled by ball with no drag : 81.5494393476 m
Distance travelled by dimpled ball with drag : 70.5145784608 m
Distance travelled by dimpled ball with drag and lift : 103.919438158 m
In [ ]: