Previously I had made a simple function that will generate a number of points/coordinates for any specified NACA 4 series aerofoil. The limitation with this approach was that it did a fairly poor job at capturing the leading-edge. The leading edge is of critical importance to the aerodynamic performance and characteristics of the aerofoil. After some consideration, I have decided not to modify this technique. I am designing the GEVfoil tools to be as modular, generic and extendable as possible. This isn’t to say I am neglecting the importance of the leading edge, or that the method is flawed.
Initially I had considered returning the aerofoil as a mathematical function, rather than a set of points. This concept may well be revisited in the future. However, most of the other input aerofoils will arrive as a set of coordinates of varying fidelity, from sources such as a the UIUC Airfoil Coordinates Database or from Comma Separated Variable (CSV) files. Therefore, the classes which will receive the aerofoil coordinate objects will deal with interpolating and appropriately converting coordinates into functions anyway. Consequently, as long as the linear-spacing is arbitrarily high enough a simple piece-wise cubic interpolation will suffice.
Note that the CSV reader itself is an abstraction of the AerofoilReader class, which I plan to later extend to read the DAT file from UIUC,
To that end, the CSV reader currently looks as follows:
import os class AerofoilReader(object): """ An abstract class to be inherited by classes for specific aerofoil file types """ def __init__(self, filename): super(AerofoilReader, self).__init__() self.coord =  self.title =  # Column titles self.label =  # Aerofoil Label foil = open(filename, 'r') self.lines = foil.readlines() class CSVReader(AerofoilReader): """ Read in CSV. Assumes a header is present.""" def __init__(self, filepath, xcol, ycol, zcol=False): super(CSVReader, self).__init__(filepath) self.label = os.path.basename(filepath).split('.') inpcols = 3 # Two or three columns if not zcol: inpcols = 2 for i, line in enumerate(self.lines): parts = line.rstrip().replace('"', '').split(',') additional =  if i > 0: # Not the header if len(parts) > inpcols: # More than x, y & z - store other columns for j in range(len(parts)): if j != xcol and j != ycol and j != zcol: additional.append(parts[j]) xyz = [parts[xcol], parts[ycol], parts[zcol]] self.coord.append(xyz + additional) else: # It's the header self.title = parts