Пример #1
0
def forceFromCoefficients(rocketState, environment, Cd, Cl, CMx, CMy, CMz,
                          CPLocation, refArea, refLength):
    ''' Initialize ForceMomentSystem from all aerodynamic coefficients '''
    q = AeroParameters.getDynamicPressure(rocketState, environment)
    if q == 0.0:
        # No force without dynamic pressure
        return ForceMomentSystem(Vector(0, 0, 0))
    nonDimConstant = q * refArea

    #### Drag ####
    localFrameAirVel = AeroParameters.getLocalFrameAirVel(
        rocketState, environment)
    dragDirection = localFrameAirVel.normalize()
    dragForce = dragDirection * Cd

    #### Lift ####
    # Find the lift force direction
    # Lift force will be in the same plane as the normalForceDirection & rocketFrameAirVel vectors
    # But, the lift force will be perpendicular to rocketFraeAirVel, whereas the normalForceDirection might form an acute angle with it
    # Rotate the normal force direction vector until it's perpendicular with rocketFrameAirVel
    normalForceDir = AeroParameters.getNormalAeroForceDirection(
        rocketState, environment)
    rotAxis = localFrameAirVel.crossProduct(normalForceDir)
    angle = math.pi / 2 - localFrameAirVel.angle(normalForceDir)
    rotatorQuat = Quaternion(rotAxis, angle)
    liftDirection = rotatorQuat.rotate(normalForceDir)
    liftForce = liftDirection * Cl

    # Combine and redimensionalize
    totalForce = (dragForce + liftForce) * nonDimConstant

    #### Moments ####
    moments = Vector(CMx, CMy, CMz) * nonDimConstant * refLength

    return ForceMomentSystem(totalForce, CPLocation, moments)
Пример #2
0
def forceFromCdCN(rocketState,
                  environment,
                  Cd,
                  CN,
                  CPLocation,
                  refArea,
                  moment=None):
    ''' 
        Convenience function for Barrowman Aero methods
        Initialize ForceMomentSystem from aerodynamic coefficients Cd and CN
        Cd should NOT already be adjusted for angle of attack 
        Moment should be a dimensional moment vector
    '''
    angleOfAttack = AeroParameters.getTotalAOA(rocketState, environment)
    q = AeroParameters.getDynamicPressure(rocketState, environment)

    #### Axial Force ####
    CA = getDragToAxialForceFactor(angleOfAttack) * Cd
    axialForce = Vector(0, 0, -CA)  #Axial force is in the negative Z-direction

    #### Normal Force ####
    normalForce = AeroParameters.getNormalAeroForceDirection(
        rocketState, environment) * CN

    totalForce = (axialForce + normalForce) * refArea * q
    return ForceMomentSystem(totalForce, CPLocation, moment)
Пример #3
0
 def getAppliedForce(self, rocketState, time, environment, CG):
     '''
         Calculates force/moment applied by the recovery system using a simple drag coefficient + area model
     '''
     #### Calculate Aero Force ####
     if self.currentStage == 0:
         # No recovery system deployed yet
         return ForceMomentSystem(Vector(0,0,0))
     else:
         # 3DoF force-only aero
         airVel = AeroParameters.getAirVelRelativeToVehicle(rocketState, environment)
         dragForceMagnitude = self.chuteCds[self.currentStage] * self.chuteAreas[self.currentStage] * AeroParameters.getDynamicPressure(rocketState, environment)
         totalForce = airVel.normalize() * dragForceMagnitude
         return ForceMomentSystem(totalForce)
Пример #4
0
 def test_dynamicPressure(self):
     self.assertAlmostEqual(AeroParameters.getDynamicPressure(self.rocketState1,self.currentConditions), self.correctDynamicPressure1)
     self.assertAlmostEqual(AeroParameters.getDynamicPressure(self.rocketState3,self.currentConditions), self.correctDynamicPressure2)
Пример #5
0
    def _barrowmanAeroFunc(self,
                           rocketState,
                           time,
                           environment,
                           precomputedData,
                           CG=Vector(0, 0, 0),
                           finDeflectionAngle=None):
        '''
            Precomputed Data is a named tuple (PreComputedFinAeroData) which contains data/results from the parts of the fin aerodynamics calculation
                that are common to all fins in a FinSet (drag calculations mostly). These calculations are performed at the FinSet level.
            Only the parts of the Fin Aero Computation that change from fin to fin (normal force mostly, since different fins can have different angles of attack) are computed here
        '''
        Mach = AeroParameters.getMachNumber(rocketState, environment)
        dynamicPressure = AeroParameters.getDynamicPressure(
            rocketState, environment)

        if finDeflectionAngle == None:
            finDeflectionAngle = self.finAngle  # Adjusted by parent finset during each timestep, when the FinSet is controlled

        # Unpack precomputedData
        airVelRelativeToFin, CPXPos, totalDragCoefficient = precomputedData

        #### Compute normal force-----------------------------------------------------------------------------------------------------------------

        # Find fin normal vector after fin deflection
        finDeflectionRotation = Quaternion(
            axisOfRotation=self.spanwiseDirection,
            angle=radians(finDeflectionAngle))
        finNormal = finDeflectionRotation.rotate(self.undeflectedFinNormal)
        # Get the tangential velocity component vector, per unit radial distance from the rocket centerline
        rollAngVel = AngularVelocity(0, 0, rocketState.angularVelocity.Z)
        unitSpanTangentialAirVelocity = rollAngVel.crossProduct(
            self.spanwiseDirection) * (-1)

        def subsonicNormalForce(Mach):  # Subsonic linear method
            tempBeta = AeroParameters.getBeta(Mach)
            CnAlpha = getFinCnAlpha_Subsonic_Barrowman(self.span,
                                                       self.planformArea,
                                                       tempBeta,
                                                       self.midChordSweep)
            return getSubsonicFinNormalForce(airVelRelativeToFin,
                                             unitSpanTangentialAirVelocity,
                                             finNormal, self.spanwiseDirection,
                                             self.CPSpanWisePosition.length(),
                                             CnAlpha, self)

        def supersonicNormalForce(Mach):  # Supersonic Busemann method
            gamma = AeroFunctions.getGamma()
            tempBeta = AeroParameters.getBeta(Mach)
            K1, K2, K3, Kstar = getBusemannCoefficients(Mach, tempBeta, gamma)

            # Mach Cone coords
            machAngle = asin(1 / Mach)
            machCone_negZPerRadius = 1 / tan(machAngle)
            machConeEdgeZPos = []
            outerRadius = self.spanSliceRadii[-1]
            for i in range(len(self.spanSliceRadii)):
                machConeAtCurrentRadius = (
                    outerRadius - self.spanSliceRadii[i]
                ) * machCone_negZPerRadius + self.tipPosition
                machConeEdgeZPos.append(machConeAtCurrentRadius)

            return getSupersonicFinNormalForce(
                airVelRelativeToFin, unitSpanTangentialAirVelocity, finNormal,
                machConeEdgeZPos, self.spanwiseDirection,
                self.CPSpanWisePosition.length(), K1, K2, K3, Kstar, self)

        if Mach <= 0.8:
            normalForceMagnitude, finMoment = subsonicNormalForce(Mach)

        elif Mach < 1.4:
            # Interpolate in transonic region
            # TODO: Do this with less function evaluations? Perhaps precompute AOA and Mach combinations and simply interpolate? Lazy precompute? Cython?
            x1, x2 = 0.8, 1.4  # Start, end pts of interpolated region
            dx = 0.001

            # Find normal force and derivative at start of interpolation interval
            f_x1, m_x1 = subsonicNormalForce(x1)
            f_x12, m_x12 = subsonicNormalForce(x1 + dx)

            # Find normal force and derivative at end of interpolation interval
            f_x2, m_x2 = supersonicNormalForce(x2)
            f_x22, m_x22 = supersonicNormalForce(x2 + dx)

            normalForceMagnitude = Interpolation.cubicInterp(
                Mach, x1, x2, f_x1, f_x2, f_x12, f_x22, dx)
            finMoment = Interpolation.cubicInterp(Mach, x1, x2, m_x1, m_x2,
                                                  m_x12, m_x22, dx)
        else:
            normalForceMagnitude, finMoment = supersonicNormalForce(Mach)

        # Complete redimensionalization of normal force coefficients by multiplying by dynamic pressure
        # Direct the normal force along the fin's normal direction
        normalForce = normalForceMagnitude * dynamicPressure * finNormal
        finMoment *= dynamicPressure

        #### Get axial force-----------------------------------------------------------------------------------------------------------------------

        avgAOA = getFinSliceAngleOfAttack(
            self.spanSliceRadii[round(len(self.spanSliceAreas) / 2)],
            airVelRelativeToFin, unitSpanTangentialAirVelocity, finNormal,
            self.spanwiseDirection,
            self.stallAngle)  # Approximate average fin AOA
        totalAxialForceCoefficient = AeroFunctions.getDragToAxialForceFactor(
            avgAOA) * totalDragCoefficient
        axialForceMagnitude = totalAxialForceCoefficient * self.rocket.Aref * dynamicPressure
        axialForceDirection = self.spanwiseDirection.crossProduct(finNormal)
        axialForce = axialForceDirection * axialForceMagnitude

        #### Get CP Location ----------------------------------------------------------------------------------------------------------------------

        CPChordWisePosition = self.position - Vector(
            0, 0, CPXPos
        )  # Ignoring the change in CP position due to fin deflection for now
        globalCP = self.CPSpanWisePosition + CPChordWisePosition

        #### Assemble total force moment system objects--------------------------------------------------------------------------------------------

        totalForce = normalForce + axialForce
        return ForceMomentSystem(totalForce,
                                 globalCP,
                                 moment=Vector(0, 0, finMoment)), globalCP
Пример #6
0
    def _getPreComputedFinAeroData(self, rocketState, environment, CG):
        #General Info ---------------------------------------------------------------------------------------------------------------------
        Aref = self.rocket.Aref
        Mach = AeroParameters.getMachNumber(rocketState, environment)
        dynamicPressure = AeroParameters.getDynamicPressure(
            rocketState, environment)

        # Skin Friction Drag -------------------------------------------------------------------------------------------------------------

        skinFrictionCoefficient = AeroFunctions.getSkinFrictionCoefficient(
            rocketState, environment, self.MACLength, Mach,
            self.surfaceRoughness, self.rocket.fullyTurbulentBL)
        # Adjust to the rocket reference area (skinFrictionCoefficient is based on wetted area)
        skinFrictionDragCoefficient = skinFrictionCoefficient * (
            self.wettedArea / Aref)
        # Correct for additional surface area due to fin thickness - Niskanen Eqn 3.85
        skinFrictionDragCoefficient *= (1 +
                                        2 * self.thickness / self.MACLength)

        # Pressure Drag ------------------------------------------------------------------------------------------------------------------

        # Leading edge drag coefficient
        if self.leadingEdgeShape == "Round":
            leadingEdgeCd = AeroFunctions.getCylinderCrossFlowCd_ZeroBaseDrag(
                Mach)
            LEthickness = self.leadingEdgeRadius * 2
        elif self.leadingEdgeShape == "Blunt":
            leadingEdgeCd = AeroFunctions.getBluntBodyCd_ZeroBaseDrag(Mach)
            LEthickness = self.leadingEdgeThickness

        # Adjust for leading edge angle and convert reference area to rocket reference area - Barrowman Eqn 4-22
        leadingEdgeCdAdjustmentFactor = LEthickness * self.span * cos(
            self.sweepAngle)**2 / Aref
        leadingEdgeCd *= leadingEdgeCdAdjustmentFactor

        # Trailing edge drag coefficient - simpler method from Niskanen section 3.4.4. Corrected to use only the trailing edge area, not the full fin frontal area
        # more intricate method available in Barrowman
        baseDragCd = AeroFunctions.getBaseDragCoefficient(Mach)
        if self.trailingEdgeShape == "Tapered":
            TEthickness = 0  # Zero base drag
        elif self.trailingEdgeShape == "Round":
            TEthickness = self.trailingEdgeRadius  # 0.5 base drag
        elif self.trailingEdgeShape == "Blunt":
            TEthickness = self.trailingEdgeThickness  # Full base drag

        # Convert to standard rocket reference Area
        trailingEdgeCd = baseDragCd * self.span * TEthickness / Aref

        # Thickness / Wave Drag
        #TODO: This section doesn't seem to be working quite right
        if Mach <= 1:
            # Thickness drag, subsonic
            thicknessDrag = 4*skinFrictionCoefficient*((self.thickness/self.rootChord)*cos(self.midChordSweep) + \
                (30 * (self.thickness/self.rootChord)**4 * cos(self.midChordSweep)**2) /  \
                    (self.subsonicFinThicknessK - Mach**2 * cos(self.midChordSweep)**2)**(3/2))
        else:
            # Supersonic wave drag
            # Using simplistic method from Hoerner - assumes diamond profile (pg 17-12, Eqn 29)
            # TODO: Implement method from Barrowman's FIN program
            thicknessDrag = 2.3 * self.aspectRatio * (self.thickness /
                                                      self.MACLength)**2

        thicknessDrag *= self.planformArea / Aref

        pressureDragCoefficient = leadingEdgeCd + trailingEdgeCd + thicknessDrag

        # Total Drag --------------------------------------------------------------------------------------------------------------------

        totalDragCoefficient = pressureDragCoefficient + skinFrictionDragCoefficient

        localFrameRocketVelocity = AeroParameters.getLocalFrameAirVel(
            rocketState, environment)
        axialPositionRelCG = self.position - CG
        finVelocityDueToRocketPitchYaw = rocketState.angularVelocity.crossProduct(
            axialPositionRelCG)
        airVelRelativeToFin = localFrameRocketVelocity - finVelocityDueToRocketPitchYaw  # The negative puts it in the wind frame
        CPXPos = self._getCPXPos(Mach)

        #### Transfer info to fins ####
        return PreComputedFinAeroData(airVelRelativeToFin, CPXPos,
                                      totalDragCoefficient)