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)
def getAppliedForce(self, state, time, environment, rocketCG): airspeed = max( AeroParameters.getLocalFrameAirVel(state, environment).length(), 0.0000001) redimConst = self.Lref / (2 * airspeed) # Calculate moment coefficients from damping coefficients localFrameAngularVelocity = Vector(*state.angularVelocity) zMomentCoeff = self.zDampingCoeffs * localFrameAngularVelocity * redimConst yMomentCoeff = self.yDampingCoeffs * localFrameAngularVelocity * redimConst xMomentCoeff = self.xDampingCoeffs * localFrameAngularVelocity * redimConst momentCoeffs = [xMomentCoeff, yMomentCoeff, zMomentCoeff] return AeroFunctions.forceFromCoefficients(state, environment, 0, 0, *momentCoeffs, self.position, self.Aref, self.Lref)
def getFinSliceArgs(self, vel, orientation, angVel, fin1, finSlicePosition=0): rocketState = RigidBodyState(Vector(0,0,0), vel, orientation, angVel) CG = self.rocket2.getCG(0, rocketState) rocketVelocity = AeroParameters.getLocalFrameAirVel(rocketState, self.currentConditions) # Add fin velocities due to motion of the rocket velocityDueToRocketPitchYaw = rocketState.angularVelocity.crossProduct(fin1.position - CG)*(-1) #The negative puts it in the wind frame #We need to find the angle between the rocket velocity vector and the plane described by the fin deflection angle #and the shaft normal finNormal = fin1.spanwiseDirection.crossProduct(Vector(0, 0, 1)) finDeflectionAngle = fin1.finAngle # Adjusted by parent finset during each timestep if(finDeflectionAngle != 0): rotation = Quaternion(axisOfRotation = fin1.spanwiseDirection, angle=math.radians(finDeflectionAngle)) finNormal = rotation.rotate(finNormal) finUnitSpanTangentialVelocity = rocketState.angularVelocity.crossProduct(fin1.spanwiseDirection)*(-1) finVelWithoutRoll = rocketVelocity + velocityDueToRocketPitchYaw return finSlicePosition, finVelWithoutRoll, finUnitSpanTangentialVelocity, finNormal, fin1.spanwiseDirection, fin1.stallAngle
def getCylindricalSkinFrictionDragCoefficientAndRollDampingMoment( state, environment, length, Mach, surfaceRoughness, wettedArea, Aref, finenessRatio, assumeFullyTurbulent=True): ''' Equations from Barrowman section 4.0, Niskanen section 3.4 ''' # Get a compressiblity-corrected flat plate skin friction drag coefficient, normalized by wetted area skinFrictionDragCoefficient = getSkinFrictionCoefficient( state, environment, length, Mach, surfaceRoughness, assumeFullyTurbulent) # Account for the fact that the rocket is cylindrical, and not a flat plate (Barrowman Eqn 4-16) skinFrictionDragCoefficient *= (1 + (0.5 / finenessRatio)) # Rebase coefficient to the reference area skinFrictionDragCoefficient *= (wettedArea / Aref) # Approximate avg radius as the radius of a cylinder which would have the same wetted area + length avgRadius = (wettedArea / length) / (2 * math.pi) rollingSurfaceVel = avgRadius * state.angularVelocity.Z # Assume velocities are perpendicular airVel = AeroParameters.getLocalFrameAirVel(state, environment).length() # Use total velocity to redimensionalize coefficient totalVelSquared = airVel**2 + rollingSurfaceVel**2 if totalVelSquared == 0: return skinFrictionDragCoefficient, Vector(0, 0, 0) else: totalSurfaceForce = skinFrictionDragCoefficient * 0.5 * totalVelSquared * environment.Density * Aref # Calculate roll damping component of total friction force using similar triangles rollDampingForce = totalSurfaceForce * (rollingSurfaceVel / math.sqrt(totalVelSquared)) # Calculate resulting moment rollDampingMoment = Vector(0, 0, -rollDampingForce * avgRadius) return skinFrictionDragCoefficient, rollDampingMoment
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)