def NACA4Blade(name,camber_root,camber_tip,camber_position,\
        thickness,bladeHeight,twistAngle,rootChord,tipChord,\
        centerOfTwist,nspan,npts):

    #//////////////////////
	#//Inputs:
	#//	camber_max: %chord
	#//	camber_position: location of max camber %chord
	#//	thickness: max thickness of airfoil as %chord
	#//	bladeHeight: height of blade
	#//	twist: degrees/unit height
	#// centerOfTwist: %chord
	#//	nspan: number of span wise subdivisions
	#//	npts: number of pts used to discretise both the upper and lower airfoil profile surface.

    #Twist per unit length
    twist=math.radians(twistAngle)/bladeHeight

    
    mr=camber_root/100
    mt=camber_tip/100
    p=camber_position/100
    t=thickness/100
    centerOfTwist[0]= centerOfTwist[0]/100
    centerOfTwist[1]/=100
    
    x=[]
    yt=[]
    yc=[]
    xu=[]
    yu=[]
    xl=[]
    yl=[]
    xuTwist=[0]*npts
    yuTwist=[0]*npts
    xlTwist=[0]*npts
    ylTwist=[0]*npts
    verts=[]
    faces=[]
    origin=(0,0,0)

    

    
    #Generate vertex coordinates with twist and scale along the span        
    dspan = bladeHeight/nspan
    for j in range(0,nspan+1): 
        
        ### bad use of memory, Fix it.  Does this matter in Python?
        x=[]
        yt=[]
        yc=[]
        xu=[]
        xl=[]
        yu=[]
        yl=[]
        
        m=(1-j/(nspan))*mr + j/nspan*mt
        
        #Generate vertex coordinates for unmodified airfoil shape
        for i in range(0,npts+1):
        
            x.append(1-math.cos(i*(math.pi/2)/npts))
            yt.append(t/0.2*(0.2969*math.pow(x[i],0.5)-0.126*x[i]-0.3516*math.pow(x[i],2)+0.2843*math.pow(x[i],3) - 0.1015*math.pow(x[i],4)))
            if(x[i]<p):
                yc.append(m/pow(p,2)*(2*p*x[i] - pow(x[i],2)))
                dycdx = 2*m/pow(p,2)*(p-x[i])
            else:
                yc.append(m/pow(1-p,2)*(1 - 2*p + 2*p*x[i] - pow(x[i],2)))
                dycdx = 2*m/pow(1-p,2)*(p-x[i])
                
            #Shift foil to center of twist
            x[i] -= centerOfTwist[0]
            yc[i] -= centerOfTwist[1]
                
            xu.append(x[i] - yt[i]*(math.sin(math.atan(dycdx))))
            yu.append(yc[i] + yt[i]*(math.cos(math.atan(dycdx))))
            xl.append(x[i] + yt[i]*(math.sin(math.atan(dycdx))))
            yl.append(yc[i] - yt[i]*(math.cos(math.atan(dycdx))))
            
            
        
        #####
           
        angle = twist*j*dspan
        chord = rootChord - j*dspan*(rootChord-tipChord)/bladeHeight	
        for i in range(0,npts):
            xuTwist[i] = xu[i]*math.cos(angle) -yu[i]*math.sin(angle)
            yuTwist[i] = xu[i]*math.sin(angle) +yu[i]*math.cos(angle)
            
            xuTwist[i] *= chord
            yuTwist[i] *= chord
            
            verts.append((xuTwist[i],yuTwist[i],j*dspan))
        for i in range(0,npts):
            xlTwist[i] = xl[i]*math.cos(angle) -yl[i]*math.sin(angle)
            ylTwist[i] = xl[i]*math.sin(angle) +yl[i]*math.cos(angle)
            
            xlTwist[i] *= chord
            ylTwist[i] *= chord
            
            verts.append((xlTwist[i],ylTwist[i],j*dspan))
    
    
    #Generate Polies from vertex IDs
    #Bottom Cap
    faces.append((0,1,npts+1))
    for i in range(0,npts-1):
        faces.append((i,i+1,npts+i+1))    
        faces.append((i,npts+i+1,npts+i))    

    #Middle
    nPerStage = npts*2
    for j in range(0,nspan):
        for i in range(0,npts-1):
            faces.append((nPerStage*j+i,nPerStage*(j+1)+i,nPerStage*(j+1)+i+1))
            faces.append((nPerStage*j+i,nPerStage*(j+1)+i+1,nPerStage*j+i+1))
        for i in range(0,npts-1):
            faces.append((nPerStage*j+i+npts,nPerStage*(j+1)+i+1+npts,nPerStage*(j+1)+i+npts))
            faces.append((nPerStage*j+i+npts,nPerStage*j+i+1+npts,nPerStage*(j+1)+i+1+npts))
        faces.append((nPerStage*j+npts-1,nPerStage*(j+1)+npts-1,nPerStage*(j+1)+npts*2-1))
        faces.append((nPerStage*j+npts-1,nPerStage*(j+1)+npts*2-1,nPerStage*(j)+npts*2-1))
    #Top Cap
    faces.append((nPerStage*(nspan),nPerStage*(nspan)+1,nPerStage*(nspan)+npts+1))
    for i in range(0,npts-1):
        faces.append((nPerStage*(nspan)+i,nPerStage*(nspan)+npts+i+1,nPerStage*(nspan)+i+1))    
        faces.append((nPerStage*(nspan)+i,nPerStage*(nspan)+npts+i,nPerStage*(nspan)+npts+i+1))    
    
    #Create Object    
    ob1 = DLUtils.createMesh(name, origin, verts, [], faces)
    
    #reset Vars
    centerOfTwist[0]*=100
    centerOfTwist[1]*=100
Exemple #2
0
def Propeller(propName,propDia,pitch,\
        hubHeight,hubDia,axleDia,\
  PropellerProps,\
        bladeTransition,nspan,npts,nBlades):

    p = PropellerProps

    res = 64  #used to define cylinder resolution.

    #initialising arrays we'll need.
    verts = []
    tmpVerts = []
    tmpVert = [0, 0, 0]
    faces = []
    origin = (0, 0, 0)
    centerOfTwist = [0, 0]

    p.rootSlope *= math.pi / 180
    p.transitionSlope *= math.pi / 180
    p.tipSlope *= math.pi / 180

    p.rootSkewSlope *= math.pi / 180
    p.transitionSkewSlope *= math.pi / 180
    p.tipSkewSlope *= math.pi / 180

    #An array of the chord lengths at each span point
    chordArray = []
    #An array of the NACA4 digits at each span point.
    NACAArray = []
    #An array that holds the position along the blade span of the airfoil cross-sections
    sPos = []
    #An array that holds the amount of skew of each airfoil cross-section
    skew = []

    #Blade root will be at center of rotation.
    bladeHeight = propDia / 2
    bladeLen = bladeHeight - bladeTransition
    dSpan = bladeHeight / nspan

    lastI = 0
    for i in range(0, nspan + 1):

        span = i * dSpan

        if (span < bladeTransition):

            #Using cubic spline interpolation.
            t = span / bladeTransition
            tm1 = 1 - t

            #root to transition point interpolation
            y1 = p.rootChord
            y2 = p.rootChord + math.sin(p.rootSlope) * p.rootStrength
            y3 = p.transitionChord - math.sin(
                p.transitionSlope) * p.transitionStrengthRoot
            y4 = p.transitionChord

            chord = pow(tm1, 3) * y1 + 3 * pow(
                tm1, 2) * t * y2 + 3 * tm1 * pow(t, 2) * y3 + pow(t, 3) * y4

            chordArray.append(chord)

            x1 = 0
            x2 = math.cos(p.rootSlope) * p.rootStrength
            x3 = (bladeTransition -
                  math.cos(p.transitionSlope) * p.transitionStrengthRoot)
            x4 = bladeTransition

            x = pow(tm1, 3) * x1 + 3 * pow(tm1, 2) * t * x2 + 3 * tm1 * pow(
                t, 2) * x3 + pow(t, 3) * x4
            sPos.append(x)

            y1 = p.rootThick
            y2 = p.rootThick + math.sin(p.rootThickSlope) * p.rootThickStrength
            y3 = p.transitionThick - math.sin(
                p.transitionThickSlope) * p.transitionThickStrengthRoot
            y4 = p.transitionThick

            thickPt = pow(tm1, 3) * y1 + 3 * pow(
                tm1, 2) * t * y2 + 3 * tm1 * pow(t, 2) * y3 + pow(t, 3) * y4
            print(thickPt)
            NACAArray.append([
                5, 3,
                int(thickPt * 10),
                int(thickPt * 100 - int(thickPt * 10) * 10)
            ])

            y1 = p.rootSkew
            y2 = p.rootSkew + math.sin(p.rootSkewSlope) * p.rootSkewStrength
            y3 = p.transitionSkew - math.sin(
                p.transitionSkewSlope) * p.transitionSkewStrengthRoot
            y4 = p.transitionSkew

            skewPt = pow(tm1, 3) * y1 + 3 * pow(
                tm1, 2) * t * y2 + 3 * tm1 * pow(t, 2) * y3 + pow(t, 3) * y4
            skew.append(skewPt)
        else:

            #Using cubic spline interpolation.
            t = (span - bladeTransition) / bladeLen
            tm1 = 1 - t

            y1 = p.transitionChord
            y2 = p.transitionChord + math.sin(
                p.transitionSlope) * p.transitionStrengthTip
            y3 = p.tipChord - math.sin(p.tipSlope) * p.tipStrength
            y4 = p.tipChord

            chord = pow(tm1, 3) * y1 + 3 * pow(
                tm1, 2) * t * y2 + 3 * tm1 * pow(t, 2) * y3 + pow(t, 3) * y4

            chordArray.append(chord)

            x1 = 0
            x2 = math.cos(p.transitionSlope) * p.transitionStrengthTip
            x3 = (bladeLen - math.cos(p.tipSlope) * p.tipStrength)
            x4 = bladeLen

            x = pow(tm1, 3) * x1 + 3 * pow(tm1, 2) * t * x2 + 3 * tm1 * pow(
                t, 2) * x3 + pow(t, 3) * x4
            sPos.append(bladeTransition + x)

            y1 = p.transitionThick
            y2 = p.transitionThick + math.sin(
                p.rootThickSlope) * p.rootThickStrength
            y3 = p.tipThick - math.sin(
                p.transitionThickSlope) * p.transitionThickStrengthTip
            y4 = p.tipThick

            thickPt = pow(tm1, 3) * y1 + 3 * pow(
                tm1, 2) * t * y2 + 3 * tm1 * pow(t, 2) * y3 + pow(t, 3) * y4

            NACAArray.append([
                5, 3,
                int(thickPt * 10),
                int(thickPt * 100 - int(thickPt * 10) * 10)
            ])

            y1 = p.transitionSkew
            y2 = p.transitionSkew + math.sin(
                p.transitionSkewSlope) * p.transitionSkewStrengthTip
            y3 = p.tipSkew - math.sin(p.tipSkewSlope) * p.tipSkewStrength
            y4 = p.tipSkew

            skewPt = pow(tm1, 3) * y1 + 3 * pow(
                tm1, 2) * t * y2 + 3 * tm1 * pow(t, 2) * y3 + pow(t, 3) * y4
            skew.append(skewPt)

    #error check of inputs
    if (len(chordArray) != nspan or len(NACAArray) != nspan):
        print(
            "The size of the array of chord lengths or the array of airfoil NACA digits does not match the number of span points used to define the blade geometry."
        )

    #Delete any existing prop geoms
    DLUtils.DeleteMesh(propName)

    #Generate Hub
    DLUtils.DrawCylinder("Hub", hubDia, 0, hubHeight,
                         res)  #use a 0.001m tolerance which we'll trim off.
    cyl = bpy.data.objects["Hub"]
    cyl.name = propName
    cyl.data.name = propName
    DLUtils.SelectOnly(propName)
    bpy.ops.transform.rotate(value=math.radians(90), axis=(0.0, 1.0, 0.0))
    DLUtils.MoveObject(propName, [2.0, 0, 0])

    areaArray = []

    #Generate vertex coordinates with twist and scale along the span
    for i in range(0, nspan + 1):
        span = sPos[i]  #i*dSpan
        if (span == 0):
            twistAngle = 0
        elif (span < bladeTransition):
            twistAngle = math.atan(
                pitch / (2 * math.pi * span)) * span / bladeTransition
        else:
            twistAngle = math.atan(pitch / (2 * math.pi * span))

        #get the airfoil profile vertices
        tmpVerts = TurboMachLib.NACA4Profile(camber=NACAArray[i][0],
                                             thickness=NACAArray[i][2] * 10 +
                                             NACAArray[i][3],
                                             camberPos=NACAArray[i][1] * 10,
                                             chord=chordArray[i],
                                             npts=npts)
        areaArray.append(0)

        centerOfTwist[0] = chordArray[i] * skew[i]
        #centerOfTwist[1] = raiseFoil
        for v in range(0, len(tmpVerts)):

            #shift all verts for twisting
            tmpVerts[v][0] -= centerOfTwist[0]
            tmpVerts[v][1] -= centerOfTwist[1]

            #Twist the airfoil vertices.  First we shift them to get the desired center of rotation.
            tmpVert[0] = tmpVerts[v][0] * math.cos(
                twistAngle) - tmpVerts[v][1] * math.sin(twistAngle)
            tmpVert[1] = tmpVerts[v][0] * math.sin(
                twistAngle) + tmpVerts[v][1] * math.cos(twistAngle)

            #shift all verts to their proper span location in Z
            tmpVert[
                2] = span + 0.01 * bladeHeight  #Shift blade by 1% of blade height to prevent poor boolean ops

            #append the vert to the master vertex list
            verts.append((tmpVert[0], tmpVert[1], tmpVert[2]))

            if (v != 0):
                dx = abs(verts[len(verts) - 1][0] - verts[len(verts) - 2][0])
                dy = abs(verts[len(verts) - 1][1] +
                         verts[len(verts) - 2][1]) / 2
                areaArray[i] += dy * dx

    #Generate Polies from vertex IDs
    #Bottom Cap
    faces.append((0, 1, npts))
    for i in range(1, npts - 1):
        faces.append((i, i + 1, npts + i))
        faces.append((i, npts + i, npts + i - 1))

    #Middle
    nPerStage = npts * 2 - 1
    for j in range(0, nspan):
        #Top Side
        for i in range(0, npts - 1):
            faces.append((nPerStage * j + i, nPerStage * (j + 1) + i,
                          nPerStage * (j + 1) + i + 1))
            faces.append((nPerStage * j + i, nPerStage * (j + 1) + i + 1,
                          nPerStage * j + i + 1))

        #First strip for bottom side (hooks to verts from top side)
        faces.append(
            (nPerStage * j, nPerStage * (j + 1) + npts, nPerStage * (j + 1)))
        faces.append(
            (nPerStage * j, nPerStage * j + npts, nPerStage * (j + 1) + npts))

        #Rest of bottom side
        for i in range(0, npts - 2):
            faces.append(
                (nPerStage * j + i + npts, nPerStage * (j + 1) + i + npts + 1,
                 nPerStage * (j + 1) + i + npts))
            faces.append(
                (nPerStage * j + i + npts, nPerStage * j + i + npts + 1,
                 nPerStage * (j + 1) + i + npts + 1))

        #Back face
        faces.append((nPerStage * j + npts - 1, nPerStage * (j + 1) + npts - 1,
                      nPerStage * (j + 1) + npts * 2 - 2))
        faces.append(
            (nPerStage * j + npts - 1, nPerStage * (j + 1) + npts * 2 - 2,
             nPerStage * (j) + npts * 2 - 2))

    #Top Cap
    faces.append((nPerStage * (nspan), nPerStage * (nspan) + 1,
                  nPerStage * (nspan) + npts))
    for i in range(1, npts - 1):
        faces.append((nPerStage * (nspan) + i, nPerStage * (nspan) + npts + i,
                      nPerStage * (nspan) + i + 1))
        faces.append(
            (nPerStage * (nspan) + i, nPerStage * (nspan) + npts + i - 1,
             nPerStage * (nspan) + npts + i))

    #Create Blender object for the blade
    dAngle = 360.0 / nBlades

    for i in range(0, nBlades):
        print("Draw Blade_" + str(i))
        blade = DLUtils.createMesh("Blade_" + str(i), origin, verts, [], faces)
        DLUtils.SelectOnly("Blade_" + str(i))
        bpy.ops.transform.rotate(value=math.radians(90), axis=(0.0, 0.0, 1.0))
        bpy.ops.transform.rotate(value=math.radians(dAngle) * i,
                                 axis=(1.0, 0.0, 0.0))

    #Union the blades to the hub
    for i in range(0, nBlades):
        print("Union: Blade_" + str(i))
        DLUtils.BooleanMesh(propName, "Blade_" + str(i), "UNION", True)

    #trim the blades to hub height
    DLUtils.DrawBox("box", hubDia * 1.1, hubDia * 1.1, hubDia * 1.1)
    DLUtils.MoveObject("box", [propDia / 2 + hubHeight / 2 + 0.001, 0, 0])
    DLUtils.BooleanMesh(propName, "box", "DIFFERENCE", True)

    #cut the axle hole
    DLUtils.DrawCylinder("hole", axleDia, 0, hubHeight * 2, res)
    DLUtils.SelectOnly("hole")
    bpy.ops.transform.rotate(value=math.radians(90), axis=(0.0, 1.0, 0.0))
    DLUtils.BooleanMesh(propName, "hole", 'DIFFERENCE', True)

    #Cut room for the axle shive
    DLUtils.DrawCylinder("ShiveHole", (hubDia - axleDia) / 4 + axleDia, 0,
                         hubHeight / 1.5, res)
    DLUtils.SelectOnly("ShiveHole")
    bpy.ops.transform.rotate(value=math.radians(90), axis=(0.0, 1.0, 0.0))
    DLUtils.BooleanMesh(propName, "ShiveHole", 'DIFFERENCE', True)
    #DLUtils.MoveObject(propName,[2.0,0,0])

    #trim the blades to the proper dia
    DLUtils.DrawCylinder("BladeTrim", propDia, 0, propDia, 256)
    DLUtils.SelectOnly("BladeTrim")
    bpy.ops.transform.rotate(value=math.radians(90), axis=(0.0, 1.0, 0.0))
    DLUtils.BooleanMesh(propName, "BladeTrim", 'INTERSECT', True)

    #done!
    print((BladeAxialStress(sPos, areaArray, 40e6, 1300, 15000)))

    return
Exemple #3
0
def NACA4Blade(name,camber_root,camber_tip,camber_position,\
        thickness,bladeHeight,twistAngle,rootChord,tipChord,\
        centerOfTwist,nspan,npts):

    #//////////////////////
    #//Inputs:
    #//	camber_max: %chord
    #//	camber_position: location of max camber %chord
    #//	thickness: max thickness of airfoil as %chord
    #//	bladeHeight: height of blade
    #//	twist: degrees/unit height
    #// centerOfTwist: %chord
    #//	nspan: number of span wise subdivisions
    #//	npts: number of pts used to discretise both the upper and lower airfoil profile surface.

    #Twist per unit length
    twist = math.radians(twistAngle) / bladeHeight

    mr = camber_root / 100
    mt = camber_tip / 100
    p = camber_position / 100
    t = thickness / 100
    centerOfTwist[0] = centerOfTwist[0] / 100
    centerOfTwist[1] /= 100

    x = []
    yt = []
    yc = []
    xu = []
    yu = []
    xl = []
    yl = []
    xuTwist = [0] * npts
    yuTwist = [0] * npts
    xlTwist = [0] * npts
    ylTwist = [0] * npts
    verts = []
    faces = []
    origin = (0, 0, 0)

    #Generate vertex coordinates with twist and scale along the span
    dspan = bladeHeight / nspan
    for j in range(0, nspan + 1):

        ### bad use of memory, Fix it.  Does this matter in Python?
        x = []
        yt = []
        yc = []
        xu = []
        xl = []
        yu = []
        yl = []

        m = (1 - j / (nspan)) * mr + j / nspan * mt

        #Generate vertex coordinates for unmodified airfoil shape
        for i in range(0, npts + 1):

            x.append(1 - math.cos(i * (math.pi / 2) / npts))
            yt.append(
                t / 0.2 *
                (0.2969 * math.pow(x[i], 0.5) - 0.126 * x[i] -
                 0.3516 * math.pow(x[i], 2) + 0.2843 * math.pow(x[i], 3) -
                 0.1015 * math.pow(x[i], 4)))
            if (x[i] < p):
                yc.append(m / pow(p, 2) * (2 * p * x[i] - pow(x[i], 2)))
                dycdx = 2 * m / pow(p, 2) * (p - x[i])
            else:
                yc.append(m / pow(1 - p, 2) *
                          (1 - 2 * p + 2 * p * x[i] - pow(x[i], 2)))
                dycdx = 2 * m / pow(1 - p, 2) * (p - x[i])

            #Shift foil to center of twist
            x[i] -= centerOfTwist[0]
            yc[i] -= centerOfTwist[1]

            xu.append(x[i] - yt[i] * (math.sin(math.atan(dycdx))))
            yu.append(yc[i] + yt[i] * (math.cos(math.atan(dycdx))))
            xl.append(x[i] + yt[i] * (math.sin(math.atan(dycdx))))
            yl.append(yc[i] - yt[i] * (math.cos(math.atan(dycdx))))

        #####

        angle = twist * j * dspan
        chord = rootChord - j * dspan * (rootChord - tipChord) / bladeHeight
        for i in range(0, npts):
            xuTwist[i] = xu[i] * math.cos(angle) - yu[i] * math.sin(angle)
            yuTwist[i] = xu[i] * math.sin(angle) + yu[i] * math.cos(angle)

            xuTwist[i] *= chord
            yuTwist[i] *= chord

            verts.append((xuTwist[i], yuTwist[i], j * dspan))
        for i in range(0, npts):
            xlTwist[i] = xl[i] * math.cos(angle) - yl[i] * math.sin(angle)
            ylTwist[i] = xl[i] * math.sin(angle) + yl[i] * math.cos(angle)

            xlTwist[i] *= chord
            ylTwist[i] *= chord

            verts.append((xlTwist[i], ylTwist[i], j * dspan))

    #Generate Polies from vertex IDs
    #Bottom Cap
    faces.append((0, 1, npts + 1))
    for i in range(0, npts - 1):
        faces.append((i, i + 1, npts + i + 1))
        faces.append((i, npts + i + 1, npts + i))

    #Middle
    nPerStage = npts * 2
    for j in range(0, nspan):
        for i in range(0, npts - 1):
            faces.append((nPerStage * j + i, nPerStage * (j + 1) + i,
                          nPerStage * (j + 1) + i + 1))
            faces.append((nPerStage * j + i, nPerStage * (j + 1) + i + 1,
                          nPerStage * j + i + 1))
        for i in range(0, npts - 1):
            faces.append(
                (nPerStage * j + i + npts, nPerStage * (j + 1) + i + 1 + npts,
                 nPerStage * (j + 1) + i + npts))
            faces.append(
                (nPerStage * j + i + npts, nPerStage * j + i + 1 + npts,
                 nPerStage * (j + 1) + i + 1 + npts))
        faces.append((nPerStage * j + npts - 1, nPerStage * (j + 1) + npts - 1,
                      nPerStage * (j + 1) + npts * 2 - 1))
        faces.append(
            (nPerStage * j + npts - 1, nPerStage * (j + 1) + npts * 2 - 1,
             nPerStage * (j) + npts * 2 - 1))
    #Top Cap
    faces.append((nPerStage * (nspan), nPerStage * (nspan) + 1,
                  nPerStage * (nspan) + npts + 1))
    for i in range(0, npts - 1):
        faces.append(
            (nPerStage * (nspan) + i, nPerStage * (nspan) + npts + i + 1,
             nPerStage * (nspan) + i + 1))
        faces.append((nPerStage * (nspan) + i, nPerStage * (nspan) + npts + i,
                      nPerStage * (nspan) + npts + i + 1))

    #Create Object
    ob1 = DLUtils.createMesh(name, origin, verts, [], faces)

    #reset Vars
    centerOfTwist[0] *= 100
    centerOfTwist[1] *= 100
def Propeller(propName,propDia,pitch,\
        hubHeight,hubDia,axleDia,\
		PropellerProps,\
        bladeTransition,nspan,npts,nBlades):
		
    p=PropellerProps    
        
    res = 64  #used to define cylinder resolution.
    
    #initialising arrays we'll need.
    verts = []
    tmpVerts = []
    tmpVert = [0,0,0]
    faces = []
    origin=(0,0,0)
    centerOfTwist = [0,0]
    
    p.rootSlope *= math.pi/180
    p.transitionSlope *= math.pi/180
    p.tipSlope *= math.pi/180
    
    p.rootSkewSlope *= math.pi/180
    p.transitionSkewSlope *= math.pi/180
    p.tipSkewSlope *= math.pi/180
    
    #An array of the chord lengths at each span point
    chordArray = []
    #An array of the NACA4 digits at each span point.
    NACAArray = []
    #An array that holds the position along the blade span of the airfoil cross-sections
    sPos = []
    #An array that holds the amount of skew of each airfoil cross-section
    skew = []

   
    #Blade root will be at center of rotation.
    bladeHeight = propDia/2 
    bladeLen = bladeHeight - bladeTransition
    dSpan = bladeHeight/nspan
    
    lastI = 0;
    for i in range(0,nspan+1):

        span = i*dSpan
        
        if(span < bladeTransition):
                        
            #Using cubic spline interpolation.
            t = span/bladeTransition
            tm1 = 1-t
            
            #root to transition point interpolation
            y1 = p.rootChord
            y2 = p.rootChord + math.sin(p.rootSlope)*p.rootStrength
            y3 = p.transitionChord - math.sin(p.transitionSlope)*p.transitionStrengthRoot
            y4 = p.transitionChord
            
            chord = pow(tm1,3)*y1 + 3*pow(tm1,2)*t*y2 + 3*tm1*pow(t,2)*y3 + pow(t,3)*y4
            
            chordArray.append(chord)
            
            x1=0
            x2=math.cos(p.rootSlope)*p.rootStrength
            x3=(bladeTransition-math.cos(p.transitionSlope)*p.transitionStrengthRoot)
            x4=bladeTransition
            
            x = pow(tm1,3)*x1 + 3*pow(tm1,2)*t*x2 + 3*tm1*pow(t,2)*x3 + pow(t,3)*x4
            sPos.append(x)
            
            
            y1 = p.rootThick
            y2 = p.rootThick + math.sin(p.rootThickSlope)*p.rootThickStrength
            y3 = p.transitionThick - math.sin(p.transitionThickSlope)*p.transitionThickStrengthRoot
            y4 = p.transitionThick
            
            thickPt = pow(tm1,3)*y1 + 3*pow(tm1,2)*t*y2 + 3*tm1*pow(t,2)*y3 + pow(t,3)*y4
            print(thickPt)
            NACAArray.append([5,3,int(thickPt*10),int(thickPt*100-int(thickPt*10)*10)])
            
            y1 = p.rootSkew
            y2 = p.rootSkew + math.sin(p.rootSkewSlope)*p.rootSkewStrength
            y3 = p.transitionSkew - math.sin(p.transitionSkewSlope)*p.transitionSkewStrengthRoot
            y4 = p.transitionSkew
            
            skewPt = pow(tm1,3)*y1 + 3*pow(tm1,2)*t*y2 + 3*tm1*pow(t,2)*y3 + pow(t,3)*y4
            skew.append(skewPt);
        else:
                
            #Using cubic spline interpolation.
            t = (span-bladeTransition)/bladeLen
            tm1 = 1-t
            
            y1 = p.transitionChord
            y2 = p.transitionChord + math.sin(p.transitionSlope)*p.transitionStrengthTip
            y3 = p.tipChord - math.sin(p.tipSlope)*p.tipStrength
            y4 = p.tipChord
            
            chord = pow(tm1,3)*y1 + 3*pow(tm1,2)*t*y2 + 3*tm1*pow(t,2)*y3 + pow(t,3)*y4
            
            chordArray.append(chord)
            
            x1=0
            x2=math.cos(p.transitionSlope)*p.transitionStrengthTip
            x3=(bladeLen-math.cos(p.tipSlope)*p.tipStrength)
            x4=bladeLen
            
            x = pow(tm1,3)*x1 + 3*pow(tm1,2)*t*x2 + 3*tm1*pow(t,2)*x3 + pow(t,3)*x4
            sPos.append(bladeTransition+x)
            
            y1 = p.transitionThick
            y2 = p.transitionThick + math.sin(p.rootThickSlope)*p.rootThickStrength
            y3 = p.tipThick - math.sin(p.transitionThickSlope)*p.transitionThickStrengthTip
            y4 = p.tipThick
            
            thickPt = pow(tm1,3)*y1 + 3*pow(tm1,2)*t*y2 + 3*tm1*pow(t,2)*y3 + pow(t,3)*y4
            
            NACAArray.append([5,3,int(thickPt*10),int(thickPt*100-int(thickPt*10)*10)])
            
            y1 = p.transitionSkew
            y2 = p.transitionSkew+ math.sin(p.transitionSkewSlope)*p.transitionSkewStrengthTip
            y3 = p.tipSkew - math.sin(p.tipSkewSlope)*p.tipSkewStrength
            y4 = p.tipSkew
            
            skewPt = pow(tm1,3)*y1 + 3*pow(tm1,2)*t*y2 + 3*tm1*pow(t,2)*y3 + pow(t,3)*y4
            skew.append(skewPt);
        
         
    
    #error check of inputs
    if(len(chordArray) != nspan or len(NACAArray) != nspan):
        print("The size of the array of chord lengths or the array of airfoil NACA digits does not match the number of span points used to define the blade geometry.")
    
    #Delete any existing prop geoms
    DLUtils.DeleteMesh(propName)
	   
    #Generate Hub   
    DLUtils.DrawCylinder("Hub",hubDia,0,hubHeight,res)#use a 0.001m tolerance which we'll trim off.
    cyl = bpy.data.objects["Hub"]
    cyl.name = propName
    cyl.data.name = propName
    DLUtils.SelectOnly(propName)
    bpy.ops.transform.rotate(value=math.radians(90),axis=(0.0,1.0,0.0))
    DLUtils.MoveObject(propName,[2.0,0,0])
    
    areaArray = []
 
    #Generate vertex coordinates with twist and scale along the span        
    for i in range(0,nspan+1): 
        span = sPos[i] #i*dSpan
        if(span == 0):
            twistAngle = 0
        elif(span < bladeTransition):
             twistAngle = math.atan(pitch/(2*math.pi*span))*span/bladeTransition
        else:
            twistAngle = math.atan(pitch/(2*math.pi*span))
        
        
        #get the airfoil profile vertices
        tmpVerts = TurboMachLib.NACA4Profile(camber=NACAArray[i][0],thickness=NACAArray[i][2]*10+NACAArray[i][3],camberPos=NACAArray[i][1]*10,chord=chordArray[i],npts=npts) 
        areaArray.append(0);
        
        centerOfTwist[0] = chordArray[i]*skew[i]
        #centerOfTwist[1] = raiseFoil
        for v in range(0,len(tmpVerts)):
            
            #shift all verts for twisting
            tmpVerts[v][0] -= centerOfTwist[0]
            tmpVerts[v][1] -= centerOfTwist[1]
           
            #Twist the airfoil vertices.  First we shift them to get the desired center of rotation.
            tmpVert[0] = tmpVerts[v][0]*math.cos(twistAngle) -tmpVerts[v][1]*math.sin(twistAngle)
            tmpVert[1] = tmpVerts[v][0]*math.sin(twistAngle) +tmpVerts[v][1]*math.cos(twistAngle)
            
            #shift all verts to their proper span location in Z
            tmpVert[2] = span+0.01*bladeHeight #Shift blade by 1% of blade height to prevent poor boolean ops
             
            #append the vert to the master vertex list
            verts.append((tmpVert[0],tmpVert[1],tmpVert[2]))
     
            if(v != 0):
                dx = abs(verts[len(verts)-1][0] - verts[len(verts)-2][0])
                dy = abs(verts[len(verts)-1][1] + verts[len(verts)-2][1])/2
                areaArray[i] +=  dy*dx
                
            

    
    
    #Generate Polies from vertex IDs
    #Bottom Cap
    faces.append((0,1,npts))
    for i in range(1,npts-1):
        faces.append((i,i+1,npts+i))    
        faces.append((i,npts+i,npts+i-1))    
    
    #Middle
    nPerStage = npts*2-1
    for j in range(0,nspan):
        #Top Side
        for i in range(0,npts-1):
            faces.append((nPerStage*j+i,nPerStage*(j+1)+i,nPerStage*(j+1)+i+1))
            faces.append((nPerStage*j+i,nPerStage*(j+1)+i+1,nPerStage*j+i+1))
        
        #First strip for bottom side (hooks to verts from top side)
        faces.append((nPerStage*j,nPerStage*(j+1)+npts,nPerStage*(j+1)))
        faces.append((nPerStage*j,nPerStage*j+npts,nPerStage*(j+1)+npts))
    
        #Rest of bottom side
        for i in range(0,npts-2):
            faces.append((nPerStage*j+i+npts,nPerStage*(j+1)+i+npts+1,nPerStage*(j+1)+i+npts))
            faces.append((nPerStage*j+i+npts,nPerStage*j+i+npts+1,nPerStage*(j+1)+i+npts+1))
        
        #Back face
        faces.append((nPerStage*j+npts-1,nPerStage*(j+1)+npts-1,nPerStage*(j+1)+npts*2-2))
        faces.append((nPerStage*j+npts-1,nPerStage*(j+1)+npts*2-2,nPerStage*(j)+npts*2-2))
    
    #Top Cap
    faces.append((nPerStage*(nspan),nPerStage*(nspan)+1,nPerStage*(nspan)+npts))
    for i in range(1,npts-1):
        faces.append((nPerStage*(nspan)+i,nPerStage*(nspan)+npts+i,nPerStage*(nspan)+i+1))    
        faces.append((nPerStage*(nspan)+i,nPerStage*(nspan)+npts+i-1,nPerStage*(nspan)+npts+i))    
    
     
    #Create Blender object for the blade
    dAngle = 360.0/nBlades
    
    for i in range(0,nBlades):
        print("Draw Blade_" + str(i))
        blade = DLUtils.createMesh("Blade_"+str(i), origin, verts, [], faces)
        DLUtils.SelectOnly("Blade_"+str(i))
        bpy.ops.transform.rotate(value=math.radians(90),axis=(0.0,0.0,1.0))
        bpy.ops.transform.rotate(value=math.radians(dAngle)*i,axis=(1.0,0.0,0.0))
        
    #Union the blades to the hub
    for i in range(0,nBlades):
        print("Union: Blade_" +str(i))
        DLUtils.BooleanMesh(propName,"Blade_"+str(i),"UNION",True) 
        
    #trim the blades to hub height
    DLUtils.DrawBox("box", hubDia*1.1,hubDia*1.1,hubDia*1.1)
    DLUtils.MoveObject("box",[propDia/2+hubHeight/2+0.001,0,0])
    DLUtils.BooleanMesh(propName,"box","DIFFERENCE",True) 
    
    #cut the axle hole   
    DLUtils.DrawCylinder("hole",axleDia,0,hubHeight*2,res)
    DLUtils.SelectOnly("hole")
    bpy.ops.transform.rotate(value=math.radians(90),axis=(0.0,1.0,0.0))
    DLUtils.BooleanMesh(propName,"hole",'DIFFERENCE',True)
    
   
    #Cut room for the axle shive
    DLUtils.DrawCylinder("ShiveHole",(hubDia-axleDia)/4+axleDia,0,hubHeight/1.5,res)
    DLUtils.SelectOnly("ShiveHole")
    bpy.ops.transform.rotate(value=math.radians(90),axis=(0.0,1.0,0.0))
    DLUtils.BooleanMesh(propName,"ShiveHole",'DIFFERENCE',True)
    #DLUtils.MoveObject(propName,[2.0,0,0])
    
    #trim the blades to the proper dia
    DLUtils.DrawCylinder("BladeTrim",propDia,0,propDia,256)
    DLUtils.SelectOnly("BladeTrim")
    bpy.ops.transform.rotate(value=math.radians(90),axis=(0.0,1.0,0.0))
    DLUtils.BooleanMesh(propName,"BladeTrim",'INTERSECT',True)
    
    #done!
    print((BladeAxialStress(sPos,areaArray, 40e6, 1300,15000)))
    
    return