diff --git a/invscidnormalpython.py b/invscidnormalpython.py
new file mode 100644
index 0000000000000000000000000000000000000000..53dbc469683e9464d07889d793b2659306e69b4b
--- /dev/null
+++ b/invscidnormalpython.py
@@ -0,0 +1,99 @@
+import numpy as np
+import matplotlib.pyplot as plt
+
+
+def constant_doublet(clockwise_coordinates, alpha, num_panels):
+    # This program finds the pressure distribution on an arbitrary aerofoil
+    # by representing the surface as a finite number of doublet panels with
+    # constant strength (Dirichlet B.C.)
+    # Original FORTRAN program by Steven Yon, 1989; Low Speed Aerodynamics, 2nd edition, p. 565
+    # Ported to Python/Numpy by Euan French, 2020.
+
+    m = num_panels
+    n = m + 1
+    al = alpha / 57.2958
+
+    # Read in the panel end points
+    ept = clockwise_coordinates  # ept[:, 0] -> x coords, ept[:, 1] -> y coords
+
+    # Convert the panelling to clockwise
+    ep = np.flip(ept, 0)
+
+    # Establish coordinates of panel end points
+    pt1 = ep[:-1, :]
+    pt2 = np.roll(ep, -1, axis=0)[:-1, :]
+
+    # Find panel angles
+    dz = pt2[:, 1] - pt1[:, 1]
+    dx = pt2[:, 0] - pt1[:, 0]
+    th = np.arctan2(dz, dx)
+
+    # Establish collocation points
+    co = ((pt2 - pt1) / 2) + pt1
+    co = co  # Needed for broadcasting
+
+    # Establish influence coefficients
+
+    # Convert collocation coordinates to local panel coordinates
+    x2t = pt2[:, 0] - pt1[:, 0]  # Note, these are equal to dz and dx at lines 20, 21
+    z2t = pt2[:, 1] - pt1[:, 1]
+    xt = co[:, 0:1] - pt1[:, 0:1].T
+    zt = co[:, 1:2] - pt1[:, 1:2].T
+
+    x2 = x2t * np.cos(th) + z2t * np.sin(th)
+    z2 = np.zeros_like(x2)
+    x = xt * np.cos(th) + zt * np.sin(th)
+    z = -xt * np.sin(th) + zt * np.cos(th)
+
+    # Save panel lengths
+    dl = x2
+
+    # Find R and theta components
+    r1 = np.sqrt(x ** 2 + z ** 2)
+    r2 = np.sqrt((x - x2) ** 2 + z ** 2)
+    th1 = np.arctan2(z, x)
+    th2 = np.arctan2(z, x - x2)
+
+    # Compute influence coeffs. A(I, J)
+    a = -0.15916 * (th2 - th1)
+    np.fill_diagonal(a, 0.5)
+
+    # Add wake influence
+    xw = co[:, 0] - pt2[m - 1, 0]  # Have to do m-1 instead of m because of the indexing offset in Python
+    zw = co[:, 1] - pt2[m - 1, 1]
+    dthw = -np.arctan(zw / xw)
+
+    a = np.concatenate((a, np.zeros(n - 1).reshape(-1, 1)), axis=1)
+    a[:, n - 1] = -0.15916 * dthw
+
+    # Build rhs vector
+    b = co[:, 0] * np.cos(al) + co[:, 1] * np.sin(al)
+    b = np.append(b, 0)
+
+    # Add an explicit Kutta condition
+    a = np.concatenate((a, np.zeros(n).reshape(1, -1)), axis=0)
+    a[n - 1, 0] = -1
+    a[n - 1, m - 1] = 1
+    a[n - 1, n - 1] = -1
+
+    # Solve for solution vector of doublet strengths
+    g = np.linalg.solve(a, b)
+
+    # Convert doublet strengths into tangential velocities
+    r = (dl[:m-2] + np.roll(dl, -1)[:m-2]) / 2
+    vel = (np.roll(g, -1)[:m-2] - g[:m-2]) / r
+    cp = 1 - vel ** 2
+
+    return cp, co
+
+
+if __name__ == "__main__":
+    vdvaerofoil = np.loadtxt('vdv15')
+    num_panels = vdvaerofoil.shape[0] - 1
+    cp, co = constant_doublet(vdvaerofoil, 5, num_panels)
+    plt.plot(co[:num_panels-2, 0], cp)
+    plt.plot(co[:, 0], co[:, 1], marker=".", linestyle='None')
+    plt.plot(vdvaerofoil[:, 0], vdvaerofoil[:, 1])
+    plt.xlim(-0.1, 1.1)
+    plt.ylim(1, -1.8)
+    plt.show()