def _NACA5cambercurve(self, MaxCamberLocFracChord, DesignLiftCoefficient): # Generates the camber curve of a NACA 5-digit airfoil xmc = MaxCamberLocFracChord # Determine the transition point m that separates the polynomial # forward section from the linear aft section R = act.cubic(-3, 6*xmc, -3*xmc**2) m = R[2].real # Sampling the chord line ChordCoord, NCosPoints = act.coslin(xmc) # As per equation (A-13) in Bill Mason's Geometry for # Aerodynamicists QQ = (3*m-7*m**2+8*m**3-4*m**4) / (math.sqrt(m*(1-m))) - 3/2*(1-2*m)*(math.pi/2-math.asin(1-2*m)) k1 = 6*DesignLiftCoefficient/QQ # Compute the two sections of the camber curve and its slope zcam = [] dzcamdx = [] for cc in ChordCoord[0:NCosPoints]: list.append(zcam, (1/6)*k1*(cc**3 - 3*m*cc**2 + m**2*(3-m)*cc)) list.append(dzcamdx, (1/6)*k1*(3*cc**2-6*m*cc+m**2*(3-m))) for cc in ChordCoord[NCosPoints:]: list.append(zcam, (1/6)*k1*m**3*(1-cc)) list.append(dzcamdx, -(1/6)*m**3); return ChordCoord, zcam, dzcamdx
def CockpitWindowContours(Height=1.620, Depth=5): P1 = [0.000, 0.076, Height - 1.620 + 2.194] P2 = [0.000, 0.852, Height - 1.620 + 2.290] P3 = [0.000, 0.904, Height + 0.037] P4 = [0.000, 0.076, Height] CWC1 = rs.AddPolyline([P1, P2, P3, P4, P1]) rs.SelectObject(CWC1) rs.Command("_FilletCorners 0.08 ") P1 = [0.000, 0.951, Height - 1.620 + 2.289] P2 = [0.000, 1.343, Height - 1.620 + 2.224] P3 = [0.000, 1.634, Height - 1.620 + 1.773] P4 = [0.000, 1.557, Height - 1.620 + 1.588] P5 = [0.000, 1.027, Height - 1.620 + 1.671] CWC2 = rs.AddPolyline([P1, P2, P3, P4, P5, P1]) rs.SelectObject(CWC2) rs.Command("_FilletCorners 0.08 ") CWC3 = act.MirrorObjectXZ(CWC1) CWC4 = act.MirrorObjectXZ(CWC2) ExtPathId = rs.AddLine([0, 0, 0], [Depth, 0, 0]) CWC1s = rs.ExtrudeCurve(CWC1, ExtPathId) CWC2s = rs.ExtrudeCurve(CWC2, ExtPathId) CWC3s = rs.ExtrudeCurve(CWC3, ExtPathId) CWC4s = rs.ExtrudeCurve(CWC4, ExtPathId) rs.DeleteObjects([CWC1, CWC2, CWC3, CWC4, ExtPathId]) return CWC1s, CWC2s, CWC3s, CWC4s
def _NACA5cambercurve(self, MaxCamberLocFracChord, DesignLiftCoefficient): # Generates the camber curve of a NACA 5-digit airfoil xmc = MaxCamberLocFracChord # Determine the transition point m that separates the polynomial # forward section from the linear aft section R = act.cubic(-3, 6 * xmc, -3 * xmc**2) m = R[2].real # Sampling the chord line ChordCoord, NCosPoints = act.coslin(xmc) # As per equation (A-13) in Bill Mason's Geometry for # Aerodynamicists QQ = (3 * m - 7 * m**2 + 8 * m**3 - 4 * m**4) / (math.sqrt( m * (1 - m))) - 3 / 2 * (1 - 2 * m) * (math.pi / 2 - math.asin(1 - 2 * m)) k1 = 6 * DesignLiftCoefficient / QQ # Compute the two sections of the camber curve and its slope zcam = [] dzcamdx = [] for cc in ChordCoord[0:NCosPoints]: list.append(zcam, (1 / 6) * k1 * (cc**3 - 3 * m * cc**2 + m**2 * (3 - m) * cc)) list.append(dzcamdx, (1 / 6) * k1 * (3 * cc**2 - 6 * m * cc + m**2 * (3 - m))) for cc in ChordCoord[NCosPoints:]: list.append(zcam, (1 / 6) * k1 * m**3 * (1 - cc)) list.append(dzcamdx, -(1 / 6) * m**3) return ChordCoord, zcam, dzcamdx
def _NACA4cambercurve(self, MaxCamberLocTenthChord, MaxCamberPercChord): """ Generates the camber curve of a NACA 4-digit airfoil """ # Using the original notation of Jacobs et al.(1933) xmc = MaxCamberLocTenthChord / 10.0 zcammax = MaxCamberPercChord / 100.0 # Protect against division by zero on airfoils like NACA0012 if xmc == 0: xmc = 0.2 # Sampling the chord line ChordCoord, NCosPoints = act.coslin(xmc) # Compute the two sections of the camber curve and its slope zcam = [] dzcamdx = [] for cc in ChordCoord[0:NCosPoints]: list.append(zcam, (zcammax / (xmc**2)) * (2 * xmc * cc - cc**2)) list.append(dzcamdx, (zcammax / xmc**2) * (2 * xmc - 2 * cc)) for cc in ChordCoord[NCosPoints:]: list.append(zcam, (zcammax / ((1 - xmc)**2)) * (1 - 2 * xmc + 2 * xmc * cc - (cc**2))) list.append(dzcamdx, (zcammax / (1 - xmc)**2) * (2 * xmc - 2 * cc)) return ChordCoord, zcam, dzcamdx
def _NACA4cambercurve(self, MaxCamberLocTenthChord, MaxCamberPercChord): """ Generates the camber curve of a NACA 4-digit airfoil """ # Using the original notation of Jacobs et al.(1933) xmc = MaxCamberLocTenthChord /10.0; zcammax = MaxCamberPercChord /100.0; # Protect against division by zero on airfoils like NACA0012 if xmc==0: xmc = 0.2 # Sampling the chord line ChordCoord, NCosPoints = act.coslin(xmc) # Compute the two sections of the camber curve and its slope zcam = [] dzcamdx = [] for cc in ChordCoord[0:NCosPoints]: list.append(zcam, (zcammax/(xmc ** 2))*(2*xmc*cc - cc ** 2)) list.append(dzcamdx, (zcammax/xmc ** 2)*(2*xmc - 2*cc)) for cc in ChordCoord[NCosPoints:]: list.append(zcam, (zcammax/((1-xmc) ** 2))*(1-2*xmc+2*xmc*cc-(cc ** 2))) list.append(dzcamdx, (zcammax/(1-xmc) ** 2)*(2*xmc - 2*cc)); return ChordCoord, zcam, dzcamdx
def _BuildSurfacePoints(self, ChordFactor, ScaleFactor): LEPoints = self._GenerateLeadingEdge() SurfacePntsUp = [] SurfacePntsDown = [] Sections = [] # SurfacePntstoAdd=[] Eps = np.linspace(0, 1, self.NPaero_span + 1) Sections = [ self.AirfoilFunct(Eps[i], LEPoints[i], self.ChordFunct, ChordFactor, self.DihedralFunct, self.TwistFunct).Curve for i in xrange(self.NPaero_span + 1) ] numb = 0 for num in xrange(self.NPaero_span + 1): section = Sections[num] sectionPnt = act.Uniform_Points_on_Curve(section, self.NPaero_chord) numb = numb + 1 for numm in xrange(1, self.NPaero_chord + 1): print(sectionPnt[numm - 1].Y()) print(sectionPnt[numm - 1].X()) edgePnts = act.make_vertex(sectionPnt[numm - 1]) if self.ScaleFactor != 1: Origin = gp_Pnt(0., 0., 0.) Pnts = act.scale_uniformal(edgePnts, Origin, self.ScaleFactor) if numm <= (self.NPaero_chord - 1) / 2 + 1: SurfacePntsUp.append(Pnts) if numm == (self.NPaero_chord - 1) / 2 + 1: SurfacePntsDown.append(Pnts) elif numm > (self.NPaero_chord - 1) / 2 + 1: # SurfacePntsDown.append(SurfacePntstoAdd) SurfacePntsDown.append(Pnts) # SurfacePntsDown=SurfacePntsD+SurfacePntstoAdd # numbb=numbb+1 # if self.ScaleFactor!= 1: # Origin = gp_Pnt(0., 0., 0.) # SurfacePnts=act.scale_uniformal(SurfacePnts, Origin, self.ScaleFactor) print(np.shape(SurfacePntsUp)) print(np.shape(SurfacePntsDown)) return SurfacePntsUp, SurfacePntsDown
def myChordFunctionJetstream(Epsilon): # User-defined function describing the variation of chord as a function of # the leading edge coordinate ChordLengths = [1, 0.333] EpsArray = [0, 1] f = act.linear_interpolation(EpsArray, ChordLengths) return f(Epsilon)
def mySweepAngleFunctionAirliner(Epsilon): # User-defined function describing the variation of sweep angle as a function # of the leading edge coordinate SweepAngles = [90, 87, 35, 35, 35, 35, 35, 35, 35, 35, 80] EpsArray = [] for i in range(0, 11): list.append(EpsArray, float(i)/10) f = act.linear_interpolation(EpsArray, SweepAngles) return f(Epsilon)
def myChordFunctionAirliner(Epsilon): # User-defined function describing the variation of chord as a function of # the leading edge coordinate ChordLengths = [0.5, 0.3792, 0.2867, 0.232, 0.1763, 0.1393, 0.1155, 0.093, 0.0713, 0.055, 0.007] EpsArray = [] for i in range(0, 11): list.append(EpsArray, float(i)/10) f = act.linear_interpolation(EpsArray, ChordLengths) return f(Epsilon)
def FuselageOML(NoseLengthRatio=0.182, TailLengthRatio=0.293, Scaling=[55.902, 55.902, 55.902], NoseCoordinates=[0, 0, 0], CylindricalMidSection=False, SimplificationReqd=False): # Instantiates a parametric fuselage outer mould line (OML) geometry for a given # set of design variables. FuselageOMLSurf, SternPoint = _BuildFuselageOML(NoseLengthRatio, TailLengthRatio, CylindricalMidSection, SimplificationReqd) if not (FuselageOMLSurf) or FuselageOMLSurf is None: return ScalingF = [0, 0, 0] ScalingF[0] = Scaling[0] / 55.902 ScalingF[1] = Scaling[1] / 55.902 ScalingF[2] = Scaling[2] / 55.902 # Overall scaling FuselageOMLSurf = act.ScaleObjectWorld000(FuselageOMLSurf, ScalingF) # A few other ways of performing the scaling... # Variant one: this depends on the current CPlane! # FuselageOMLSurf = rs.ScaleObject(FuselageOMLSurf, (0,0,0), Scaling) # Variant two: define plane in World coordinates #P = rs.PlaneFromFrame((0,0,0),(1,0,0),(0,1,0)) #TransfMatrix = Rhino.Geometry.Transform.Scale(P, Scaling[0], Scaling[1], Scaling[2]) #FuselageOMLSurf = rs.TransformObjects(FuselageOMLSurf, TransfMatrix) # Variant three: World coordinate system based scaling #xform = rs.XformScale(Scaling) #FuselageOMLSurf = rs.TransformObjects(FuselageOMLSurf, xform) SternPoint[0] = SternPoint[0] * ScalingF[0] SternPoint[1] = SternPoint[1] * ScalingF[1] SternPoint[2] = SternPoint[2] * ScalingF[2] # Positioning MoveVec = rs.VectorCreate(NoseCoordinates, [0, 0, 0]) FuselageOMLSurf = rs.MoveObject(FuselageOMLSurf, MoveVec) SternPoint[0] = SternPoint[0] + NoseCoordinates[0] SternPoint[1] = SternPoint[1] + NoseCoordinates[1] SternPoint[2] = SternPoint[2] + NoseCoordinates[2] return FuselageOMLSurf, SternPoint
def _FuselageLongitudinalGuideCurves(NoseLengthRatio, TailLengthRatio): # Internal function. Defines the four longitudinal curves that outline the # fuselage (outer mould line). FSVU, FSVL = _AirlinerFuselageSideView(NoseLengthRatio, TailLengthRatio) FSVUCurve = rs.AddCurve(FSVU) FSVLCurve = rs.AddCurve(FSVL) AFPVPort, NoseEndX, TailStartX = _AirlinerFuselagePlanView( NoseLengthRatio, TailLengthRatio) # Generate plan view PlanPortCurve = rs.AddCurve(AFPVPort) # How wide is the fuselage? (Xmin, Ymin, Zmin, Xmax, Ymax, Zmax) = act.ObjectsExtents(PlanPortCurve) # Generate a slightly wider projection surface FSVMeanCurve = rs.MeanCurve(FSVUCurve, FSVLCurve) RuleLinePort = rs.AddLine((0, 0, 0), (0, -1.1 * abs(Ymax - Ymin), 0)) FSVMCEP = rs.CurveEndPoint(FSVMeanCurve) AftLoftEdgePort = rs.CopyObject(RuleLinePort, FSVMCEP) ParallelLoftEdgePort = rs.CopyObject(FSVMeanCurve, (0, -1.1 * abs(Ymax - Ymin), 0)) LSPort = rs.AddSweep2((FSVMeanCurve, ParallelLoftEdgePort), (RuleLinePort, AftLoftEdgePort)) # Project the plan view onto the mean surface PortCurve = rs.ProjectCurveToSurface(PlanPortCurve, LSPort, (0, 0, 100)) # House-keeping rs.DeleteObjects([ LSPort, PlanPortCurve, ParallelLoftEdgePort, RuleLinePort, AftLoftEdgePort ]) # Tidy up the mean curve. This is necessary for a smooth result and removing # it can render the algorithm unstable. However, FitCurve itself may sometimes # be slightly unstable. FLength = abs(Xmax - Xmin) # establish a reference length PortCurveSimplified = rs.FitCurve(PortCurve, distance_tolerance=FLength * 0.001) StarboardCurveSimplified = act.MirrorObjectXZ(PortCurveSimplified) rs.DeleteObject(PortCurve) # Compute the actual end points of the longitudinal curves (Xmin, Ymin, Zmin, Xmax1, Ymax, Zmax) = act.ObjectsExtents(StarboardCurveSimplified) (Xmin, Ymin, Zmin, Xmax2, Ymax, Zmax) = act.ObjectsExtents(PortCurveSimplified) (Xmin, Ymin, Zmin, Xmax3, Ymax, Zmax) = act.ObjectsExtents(FSVUCurve) (Xmin, Ymin, Zmin, Xmax4, Ymax, Zmax) = act.ObjectsExtents(FSVLCurve) EndX = min([Xmax1, Xmax2, Xmax3, Xmax4]) return StarboardCurveSimplified, PortCurveSimplified, FSVUCurve, FSVLCurve, FSVMeanCurve, NoseEndX, TailStartX, EndX
def GenerateLiftingSurface(self, ChordFactor, ScaleFactor, OptimizeChordScale=0): # This is the main method of this class. It builds a lifting surface # (wing, tailplane, etc.) with the given ChordFactor and ScaleFactor or # an optimized ChordFactor and ScaleFactor, with the local search started # from the two given values. x0 = [ChordFactor, ScaleFactor] if OptimizeChordScale: self._CheckOptParCorrectlySpec() self._NormaliseWeightings() self._PrintTargetsAndWeights() print("Optimizing scale factors...") # An iterative local hillclimber type optimizer is needed here. One # option might be SciPy's fmin as below: # x0, fopt, iter, funcalls, warnflag, allvecs = scipy.optimize.fmin(self._LSObjective, x0, retall=True, xtol=0.025, full_output=True) # However, SciPy is not supported on 64-bit Rhino installations, so # so here we use an alternative: a simple evoltionary optimizer # included with AirCONICS_tools. MaxIter = 50 xtol = 0.025 deltax = [x0[0]*0.25,x0[1]*0.25] x0, fopt = act.boxevopmin2d(self._LSObjective, x0, deltax, xtol, MaxIter) x0[0] = abs(x0[0]) x0[1] = abs(x0[1]) print("Optimum chord factor %5.3f, optimum scale factor %5.3f" % (x0[0], x0[1])) LS, ActualSemiSpan, LSP_area, RootChord, AR, WingTip = self._BuildLS(x0[0], x0[1]) self._ClearConstructionGeometry() # Moving the wing into position AP = rs.AddPoint(self.ApexPoint) MoveVec = rs.VectorCreate(AP, (0,0,0)) rs.MoveObject(LS, MoveVec) if WingTip: rs.MoveObject(WingTip, MoveVec) rs.DeleteObject(AP) # FINAL REPORT ON THE COMPLETED SURFACE SA = rs.SurfaceArea(LS) print("Wing complete. Key features (all related to both wings):") print("Proj.area: %5.4f Wet.area: %5.4f Span:%5.4f Aspect ratio: %5.4f Root chord: %5.4f" % (2*LSP_area, 2.0*SA[0], 2.0*ActualSemiSpan, AR, RootChord)) return LS, ActualSemiSpan, LSP_area, RootChord, AR, WingTip
def _BuildLS(self, ChordFactor, ScaleFactor): """Generates a tentative lifting surface, given the general, nondimensional parameters of the object (variations of chord length, dihedral, etc.) and the two scaling factors. Parameters ---------- ChordFactor - float Factor againt which the standard shape is scaled in the chordwise direction ScaleFactor - float Factor againt which the standard shape is scaled in the world coordinates Returns ------- LS : TopoDS_Shape The generated Lifting surface ActualSemiSpan : scalar TODO: currently not calculated, None is returned LSP_area : scalar TODO: currently not calculated, None is returned AR : scalar TODO: currently not calculated, None is returned WingTip : TopoDS face or shape TODO: currently not calculated, None is returned """ LEPoints = self._GenerateLeadingEdge() Sections = [] # TODO: These lists are used for when the curve has been smoothed or # the loft has failed, neither of which have been implemented yet # ProjectedSections = [] # TEPoints_u = [] # TEPoints_l = [] Eps = np.linspace(0, 1, self.SegmentNo + 1) Sections = [ self.AirfoilFunct(Eps[i], LEPoints[i], self.ChordFunct, ChordFactor, self.DihedralFunct, self.TwistFunct).Curve for i in xrange(self.SegmentNo + 1) ] self._Sections = Sections # I used from debugging - remove it? # TODO: Implement chord projection and Curve start/end points # to rescale smoothed curves and for secondary loft methods LS = act.AddSurfaceLoft(Sections, max_degree=self._max_degree, continuity=self._Cont) if LS is None: pass WingTip = None if self.TipRequired: pass # Scaling if self.ScaleFactor != 1: Origin = gp_Pnt(0., 0., 0.) LS = act.scale_uniformal(LS, Origin, self.ScaleFactor) # TODO: Wing tip scaling (TipRequired is not implemented yet) if self.TipRequired and WingTip: pass self.RootChord = (self.ChordFunct(0) * ChordFactor) * ScaleFactor # Temporarily set other variables as None until above TODO's are done ActualSemiSpan = None LSP_area = None AR = None return LS, ActualSemiSpan, LSP_area, AR, WingTip
def _TransformAirfoil(self, C): # Internal function. Given a normal airfoil, unit chord, nose in origin, # chord along x axis, applies scaling, rotations, positioning and smoothing # Smoothing for i in range(1, self.SmoothingIterations + 1): rs.FairCurve(C) # Find the actual leading edge point - the airfoil may have stretched as # as a result of the smoothing or it may have been incorrectly defined # through a series of coordinates RefLine = rs.AddLine((-100, 0, -100), (-100, 0, 100)) ClosestPoints = rs.CurveClosestObject(RefLine, C) P = rs.AddPoint(ClosestPoints[1]) MoveVec = rs.VectorCreate((0, 0, 0), P) rs.MoveObject(C, MoveVec) # Garbage collection rs.DeleteObjects((RefLine, P)) # Now find the trailing edge points PUpper = rs.CurveStartPoint(C) PLower = rs.CurveEndPoint(C) TECentre = ((PUpper[0] + PLower[0]) / 2, (PUpper[1] + PLower[1]) / 2, (PUpper[2] + PLower[2]) / 2) if PUpper[2] < PLower[2]: print "Warning: the upper and lower surface intersect at the TE." TECentrePoint = rs.AddPoint(TECentre) AxisOfRotation = rs.VectorCreate((0, 0, 0), (0, 1, 0)) L1 = rs.AddLine((0, 0, 0), (1, 0, 0)) L2 = rs.AddLine((0, 0, 0), TECentrePoint) AngRot = rs.Angle2(L1, L2) # The angle returned by Angle2 is always positive so: if TECentre[2] < 0: rs.RotateObject(C, (0, 0, 0), AngRot[0], AxisOfRotation) else: rs.RotateObject(C, (0, 0, 0), -AngRot[0], AxisOfRotation) # Garbage collection rs.DeleteObjects((TECentrePoint, L1, L2)) # Find the trailing edge point again after rotating it onto the x axis PUpper = rs.CurveStartPoint(C) PLower = rs.CurveEndPoint(C) TECentre = [(PUpper[0] + PLower[0]) / 2, (PUpper[1] + PLower[1]) / 2, (PUpper[2] + PLower[2]) / 2] ActualChordLength = TECentre[0] # Scale the airfoil to unit chord #rs.ScaleObject(C, (0,0,0), (1/ActualChordLength, 1, 1/ActualChordLength)) act.ScaleObjectWorld000( C, (1 / ActualChordLength, 1, 1 / ActualChordLength)) # Now we can assume that airfoil is normalised to the unit chord, with # its leading edge in the origin, trailing edge in (1,0,0) Chrd = rs.AddLine((0, 0, 0), (1, 0, 0)) # Scaling ScaleFact = (self.ChordLength, self.ChordLength, self.ChordLength) # same as rs.ScaleObject(C, (0,0,0), ScaleFact) act.ScaleObjectWorld000(C, ScaleFact) # same as rs.ScaleObject(Chrd, (0,0,0), ScaleFact) act.ScaleObjectWorld000(Chrd, ScaleFact) # Twist rs.RotateObject(C, (0, 0, 0), self.Twist, rs.VectorCreate((0, 0, 0), (0, 1, 0))) rs.RotateObject(Chrd, (0, 0, 0), self.Twist, rs.VectorCreate((0, 0, 0), (0, 1, 0))) # Dihedral rs.RotateObject(C, (0, 0, 0), -self.Rotation, rs.VectorCreate((0, 0, 0), (1, 0, 0))) rs.RotateObject(Chrd, (0, 0, 0), -self.Rotation, rs.VectorCreate((0, 0, 0), (1, 0, 0))) # 3d positioning MoveVec = rs.VectorCreate(self.LeadingEdgePoint, (0, 0, 0)) rs.MoveObject(C, MoveVec) rs.MoveObject(Chrd, MoveVec) return C, Chrd
def TurbofanNacelle(EngineSection, Chord, CentreLocation=[0, 0, 0], ScarfAngle=3, HighlightRadius=1.45, MeanNacelleLength=5.67): # The defaults yield a nacelle similar to that of an RR Trent 1000 / GEnx HighlightDepth = 0.12 * MeanNacelleLength SectionNo = 100 # Draw the nacelle with the centre of the intake highlight circle in 0,0,0 rs.EnableRedraw(False) Highlight = rs.AddCircle3Pt((0, 0, HighlightRadius), (0, -HighlightRadius, 0), (0, 0, -HighlightRadius)) HighlightCutterCircle = rs.AddCircle3Pt((0, 0, HighlightRadius * 1.5), (0, -HighlightRadius * 1.5, 0), (0, 0, -HighlightRadius * 1.5)) # Fan disk for CFD boundary conditions FanCircle = rs.CopyObject(Highlight, (MeanNacelleLength * 0.25, 0, 0)) FanDisk = rs.AddPlanarSrf(FanCircle) # Aft outflow for CFD boundary conditions BypassCircle = rs.CopyObject(Highlight, (MeanNacelleLength * 0.85, 0, 0)) BypassDisk = rs.AddPlanarSrf(BypassCircle) rs.DeleteObjects([FanCircle, BypassCircle]) # Outflow cone TailConeBasePoint = [MeanNacelleLength * 0.84, 0, 0] TailConeApex = [MeanNacelleLength * 1.35, 0, 0] TailConeRadius = HighlightRadius * 0.782 TailCone = rs.AddCone(TailConeBasePoint, TailConeApex, TailConeRadius) # Spinner cone SpinnerConeBasePoint = [MeanNacelleLength * 0.26, 0, 0] SpinnerConeApex = [MeanNacelleLength * 0.08, 0, 0] SpinnerConeRadius = MeanNacelleLength * 0.09 Spinner = rs.AddCone(SpinnerConeBasePoint, SpinnerConeApex, SpinnerConeRadius) # Tilt the intake RotVec = rs.VectorCreate((0, 0, 0), (0, 1, 0)) Highlight = rs.RotateObject(Highlight, (0, 0, 0), ScarfAngle, axis=RotVec) # Set up the disk for separating the intake lip later HighlightCutterCircle = rs.RotateObject(HighlightCutterCircle, (0, 0, 0), ScarfAngle, axis=RotVec) HighlightCutterDisk = rs.AddPlanarSrf(HighlightCutterCircle) rs.DeleteObject(HighlightCutterCircle) rs.MoveObject(HighlightCutterDisk, (HighlightDepth, 0, 0)) # Build the actual airfoil sections to define the nacelle HighlightPointVector = rs.DivideCurve(Highlight, SectionNo) Sections = [] TailPoints = [] Rotation = 0 Twist = 0 AirfoilSeligName = 'goe613' SmoothingPasses = 1 for HighlightPoint in HighlightPointVector: ChordLength = MeanNacelleLength - HighlightPoint.X Af = primitives.Airfoil(HighlightPoint, ChordLength, Rotation, Twist, airconics_setup.SeligPath) AfCurve, Chrd = primitives.Airfoil.AddAirfoilFromSeligFile( Af, AirfoilSeligName, SmoothingPasses) rs.DeleteObject(Chrd) P = rs.CurveEndPoint(AfCurve) list.append(TailPoints, P) AfCurve = act.AddTEtoOpenAirfoil(AfCurve) list.append(Sections, AfCurve) Rotation = Rotation + 360.0 / SectionNo list.append(TailPoints, TailPoints[0]) # Build the actual nacelle OML surface EndCircle = rs.AddInterpCurve(TailPoints) Nacelle = rs.AddSweep2([Highlight, EndCircle], Sections, closed=True) # Separate the lip Cowling, HighlightSection = rs.SplitBrep(Nacelle, HighlightCutterDisk, True) # Now build the pylon between the engine and the specified chord on the wing CP1 = [ MeanNacelleLength * 0.26 + CentreLocation[0], CentreLocation[1], CentreLocation[2] + HighlightRadius * 0.1 ] CP2 = [ MeanNacelleLength * 0.4 + CentreLocation[0], CentreLocation[1], HighlightRadius * 1.45 + CentreLocation[2] ] CP3 = rs.CurveEndPoint(Chord) rs.ReverseCurve(Chord) CP4 = rs.CurveEndPoint(Chord) # Move the engine into its actual place on the wing rs.MoveObjects( [HighlightSection, Cowling, FanDisk, BypassDisk, TailCone, Spinner], CentreLocation) # Pylon wireframe PylonTop = rs.AddInterpCurve([CP1, CP2, CP3, CP4]) PylonAf = primitives.Airfoil(CP1, MeanNacelleLength * 1.35, 90, 0, airconics_setup.SeligPath) PylonAfCurve, PylonChord = primitives.Airfoil.AddNACA4( PylonAf, 0, 0, 12, 3) LowerTE = rs.CurveEndPoint(PylonChord) PylonTE = rs.AddLine(LowerTE, CP4) # Create the actual pylon surface PylonLeft = rs.AddNetworkSrf([PylonTop, PylonAfCurve, PylonTE]) rs.MoveObject(PylonLeft, (0, -CentreLocation[1], 0)) PylonRight = act.MirrorObjectXZ(PylonLeft) rs.MoveObject(PylonLeft, (0, CentreLocation[1], 0)) rs.MoveObject(PylonRight, (0, CentreLocation[1], 0)) PylonAfCurve = act.AddTEtoOpenAirfoil(PylonAfCurve) PylonAfSrf = rs.AddPlanarSrf(PylonAfCurve) # Assigning basic surface properties act.AssignMaterial(Cowling, "ShinyBABlueMetal") act.AssignMaterial(HighlightSection, "UnpaintedMetal") act.AssignMaterial(TailCone, "UnpaintedMetal") act.AssignMaterial(FanDisk, "FanDisk") act.AssignMaterial(Spinner, "ShinyBlack") act.AssignMaterial(BypassDisk, "FanDisk") act.AssignMaterial(PylonLeft, "White_composite_external") act.AssignMaterial(PylonRight, "White_composite_external") # Clean-up rs.DeleteObject(HighlightCutterDisk) rs.DeleteObjects(Sections) rs.DeleteObject(EndCircle) rs.DeleteObject(Highlight) rs.DeleteObjects([PylonTop, PylonAfCurve, PylonChord, PylonTE]) rs.Redraw() TFEngine = [ Cowling, HighlightSection, TailCone, FanDisk, Spinner, BypassDisk ] TFPylon = [PylonLeft, PylonRight, PylonAfSrf] return TFEngine, TFPylon
tea.myAirfoilFunctionAirliner, LooseSurf, SegmentNo, TipRequired=True) ChordFactor = 1 ScaleFactor = 50 rs.EnableRedraw(False) WingSurf, ActualSemiSpan, LSP_area, RootChord, AR, WingTip = Wing.GenerateLiftingSurface( ChordFactor, ScaleFactor) rs.EnableRedraw() SpanStation = 0.3 # The engine is to be placed at 30% span EngineDia = 2.9 NacelleLength = 1.95 * EngineDia rs.EnableRedraw(False) EngineSection, Chord = act.CutSect(WingSurf, SpanStation) CEP = rs.CurveEndPoint(Chord) # Variables controlling the position of the engine with respect to the wing EngineCtrFwdOfLE = 0.98 EngineCtrBelowLE = 0.35 Scarf_deg = 4 # Now build the engine and its pylon EngineStbd, PylonStbd = TurbofanNacelle( EngineSection, Chord, CentreLocation=[ CEP.X - EngineCtrFwdOfLE * NacelleLength, CEP.Y, CEP.Z - EngineCtrBelowLE * NacelleLength ],
def _BuildFuselageOML(NoseLengthRatio, TailLengthRatio, CylindricalMidSection, SimplificationReqd): MaxFittingAttempts = 6 FittingAttempts = -1 NetworkSrfSettings = [ [35, 20, 15, 5, 20], [35, 30, 15, 5, 20], [35, 20, 15, 2, 20], [30, 30, 15, 2, 20], [30, 20, 15, 2, 20], [25, 20, 15, 2, 20], [20, 20, 15, 2, 20], [15, 20, 15, 2, 20]] StarboardCurve, PortCurve, FSVUCurve, FSVLCurve, FSVMeanCurve, NoseEndX, TailStartX, EndX = _FuselageLongitudinalGuideCurves(NoseLengthRatio, TailLengthRatio) while FittingAttempts <= MaxFittingAttempts: FittingAttempts = FittingAttempts + 1 # Construct array of cross section definition frames SX0 = 0 Step01 = NetworkSrfSettings[FittingAttempts][0] SX1 = 0.04*NoseEndX Step12 = NetworkSrfSettings[FittingAttempts][1] SX2 = SX1 + 0.25*NoseEndX Step23 = NetworkSrfSettings[FittingAttempts][2] SX3 = NoseEndX Step34 = NetworkSrfSettings[FittingAttempts][3] SX4 = TailStartX Step45 = NetworkSrfSettings[FittingAttempts][4] SX5 = EndX print "Attempting network surface fit with network density setup ", NetworkSrfSettings[FittingAttempts][:] Stations01 = act.pwfrange(SX0,SX1,max([Step01,2])) Stations12 = act.pwfrange(SX1,SX2,max([Step12,2])) Stations23 = act.pwfrange(SX2,SX3,max([Step23,2])) Stations34 = act.pwfrange(SX3,SX4,max([Step34,2])) Stations45 = act.pwfrange(SX4,SX5,max([Step45,2])) StationRange = Stations01[:-1] + Stations12[:-1] + Stations23[:-1] + Stations34[:-1] + Stations45 C = [] FirstTime = True for XStation in StationRange: P = rs.PlaneFromPoints((XStation,0,0),(XStation,1,0),(XStation,0,1)) IP1 = rs.PlaneCurveIntersection(P,StarboardCurve) IP2 = rs.PlaneCurveIntersection(P,FSVUCurve) IP3 = rs.PlaneCurveIntersection(P,PortCurve) IP4 = rs.PlaneCurveIntersection(P,FSVLCurve) IPcentre = rs.PlaneCurveIntersection(P,FSVMeanCurve) IPoint1 = rs.AddPoint(IP1[0][1]) IPoint2 = rs.AddPoint(IP2[0][1]) IPoint3 = rs.AddPoint(IP3[0][1]) IPoint4 = rs.AddPoint(IP4[0][1]) IPointCentre = rs.AddPoint(IPcentre[0][1]) PseudoDiameter = abs(IP4[0][1].Z-IP2[0][1].Z) if CylindricalMidSection and NoseEndX < XStation < TailStartX: # Ensure that the parallel section of the fuselage is cylindrical # if CylindricalMidSection is True print "Enforcing circularity in the central section..." if FirstTime: PseudoRadius = PseudoDiameter/2 FirstTime = False Pc = rs.PointCoordinates(IPointCentre) P1 = P2 = P3 = Pc P1 = rs.PointAdd(P1,(0,PseudoRadius,0)) P2 = rs.PointAdd(P2,(0,0,PseudoRadius)) P3 = rs.PointAdd(P3,(0,-PseudoRadius,0)) c = rs.AddCircle3Pt(P1, P2, P3) else: c = rs.AddInterpCurve([IPoint1,IPoint2,IPoint3,IPoint4,IPoint1],knotstyle=3) # Once CSec is implemented in Rhino Python, this could be replaced rs.DeleteObjects([IPoint1,IPoint2,IPoint3,IPoint4,IPointCentre]) list.append(C,c) # Fit fuselage external surface CurveNet = [] for c in C[1:]: list.append(CurveNet,c) list.append(CurveNet, FSVUCurve) list.append(CurveNet, PortCurve) list.append(CurveNet, FSVLCurve) list.append(CurveNet, StarboardCurve) FuselageOMLSurf = rs.AddNetworkSrf(CurveNet) rs.DeleteObjects(C) if not(FuselageOMLSurf==None): print "Network surface fit succesful on attempt ", FittingAttempts+1 FittingAttempts = MaxFittingAttempts+1 # Force an exit from 'while' # If all attempts at fitting a network surface failed, we attempt a Sweep2 if FuselageOMLSurf==None: print "Failed to fit network surface to the external shape of the fuselage" print "Attempting alternative fitting method, quality likely to be low..." try: FuselageOMLSurf = rs.AddSweep2([FSVUCurve,FSVLCurve],C[:]) except: FuselageOMLSurf = False SimplificationReqd = True # Enforce simplification if not(FuselageOMLSurf): print "Alternative fitting method failed too. Out of ideas." if FuselageOMLSurf and SimplificationReqd: rs.UnselectAllObjects() rs.SelectObject(FuselageOMLSurf) ToleranceStr = str(0.0005*EndX) print "Smoothing..." rs.Command("FitSrf " + ToleranceStr) rs.UnselectAllObjects() # Compute the stern point coordinates of the fuselage Pu = rs.CurveEndPoint(FSVUCurve) Pl = rs.CurveEndPoint(FSVLCurve) SternPoint = [Pu.X, Pu.Y, 0.5*(Pu.Z+Pl.Z)] rs.DeleteObjects([FSVUCurve,FSVLCurve,PortCurve,StarboardCurve,FSVMeanCurve]) return FuselageOMLSurf, SternPoint
def _BuildFuselageOML(NoseLengthRatio, TailLengthRatio, CylindricalMidSection, SimplificationReqd): MaxFittingAttempts = 6 FittingAttempts = -1 NetworkSrfSettings = [[35, 20, 15, 5, 20], [35, 30, 15, 5, 20], [35, 20, 15, 2, 20], [30, 30, 15, 2, 20], [30, 20, 15, 2, 20], [25, 20, 15, 2, 20], [20, 20, 15, 2, 20], [15, 20, 15, 2, 20]] StarboardCurve, PortCurve, FSVUCurve, FSVLCurve, FSVMeanCurve, NoseEndX, TailStartX, EndX = _FuselageLongitudinalGuideCurves( NoseLengthRatio, TailLengthRatio) while FittingAttempts <= MaxFittingAttempts: FittingAttempts = FittingAttempts + 1 # Construct array of cross section definition frames SX0 = 0 Step01 = NetworkSrfSettings[FittingAttempts][0] SX1 = 0.04 * NoseEndX Step12 = NetworkSrfSettings[FittingAttempts][1] SX2 = SX1 + 0.25 * NoseEndX Step23 = NetworkSrfSettings[FittingAttempts][2] SX3 = NoseEndX Step34 = NetworkSrfSettings[FittingAttempts][3] SX4 = TailStartX Step45 = NetworkSrfSettings[FittingAttempts][4] SX5 = EndX print "Attempting network surface fit with network density setup ", NetworkSrfSettings[ FittingAttempts][:] Stations01 = act.pwfrange(SX0, SX1, max([Step01, 2])) Stations12 = act.pwfrange(SX1, SX2, max([Step12, 2])) Stations23 = act.pwfrange(SX2, SX3, max([Step23, 2])) Stations34 = act.pwfrange(SX3, SX4, max([Step34, 2])) Stations45 = act.pwfrange(SX4, SX5, max([Step45, 2])) StationRange = Stations01[: -1] + Stations12[: -1] + Stations23[: -1] + Stations34[: -1] + Stations45 C = [] FirstTime = True for XStation in StationRange: P = rs.PlaneFromPoints((XStation, 0, 0), (XStation, 1, 0), (XStation, 0, 1)) IP1 = rs.PlaneCurveIntersection(P, StarboardCurve) IP2 = rs.PlaneCurveIntersection(P, FSVUCurve) IP3 = rs.PlaneCurveIntersection(P, PortCurve) IP4 = rs.PlaneCurveIntersection(P, FSVLCurve) IPcentre = rs.PlaneCurveIntersection(P, FSVMeanCurve) IPoint1 = rs.AddPoint(IP1[0][1]) IPoint2 = rs.AddPoint(IP2[0][1]) IPoint3 = rs.AddPoint(IP3[0][1]) IPoint4 = rs.AddPoint(IP4[0][1]) IPointCentre = rs.AddPoint(IPcentre[0][1]) PseudoDiameter = abs(IP4[0][1].Z - IP2[0][1].Z) if CylindricalMidSection and NoseEndX < XStation < TailStartX: # Ensure that the parallel section of the fuselage is cylindrical # if CylindricalMidSection is True print "Enforcing circularity in the central section..." if FirstTime: PseudoRadius = PseudoDiameter / 2 FirstTime = False Pc = rs.PointCoordinates(IPointCentre) P1 = P2 = P3 = Pc P1 = rs.PointAdd(P1, (0, PseudoRadius, 0)) P2 = rs.PointAdd(P2, (0, 0, PseudoRadius)) P3 = rs.PointAdd(P3, (0, -PseudoRadius, 0)) c = rs.AddCircle3Pt(P1, P2, P3) else: c = rs.AddInterpCurve( [IPoint1, IPoint2, IPoint3, IPoint4, IPoint1], knotstyle=3) # Once CSec is implemented in Rhino Python, this could be replaced rs.DeleteObjects( [IPoint1, IPoint2, IPoint3, IPoint4, IPointCentre]) list.append(C, c) # Fit fuselage external surface CurveNet = [] for c in C[1:]: list.append(CurveNet, c) list.append(CurveNet, FSVUCurve) list.append(CurveNet, PortCurve) list.append(CurveNet, FSVLCurve) list.append(CurveNet, StarboardCurve) FuselageOMLSurf = rs.AddNetworkSrf(CurveNet) rs.DeleteObjects(C) if not (FuselageOMLSurf == None): print "Network surface fit succesful on attempt ", FittingAttempts + 1 FittingAttempts = MaxFittingAttempts + 1 # Force an exit from 'while' # If all attempts at fitting a network surface failed, we attempt a Sweep2 if FuselageOMLSurf == None: print "Failed to fit network surface to the external shape of the fuselage" print "Attempting alternative fitting method, quality likely to be low..." try: FuselageOMLSurf = rs.AddSweep2([FSVUCurve, FSVLCurve], C[:]) except: FuselageOMLSurf = False SimplificationReqd = True # Enforce simplification if not (FuselageOMLSurf): print "Alternative fitting method failed too. Out of ideas." if FuselageOMLSurf and SimplificationReqd: rs.UnselectAllObjects() rs.SelectObject(FuselageOMLSurf) ToleranceStr = str(0.0005 * EndX) print "Smoothing..." rs.Command("FitSrf " + ToleranceStr) rs.UnselectAllObjects() # Compute the stern point coordinates of the fuselage Pu = rs.CurveEndPoint(FSVUCurve) Pl = rs.CurveEndPoint(FSVLCurve) SternPoint = [Pu.X, Pu.Y, 0.5 * (Pu.Z + Pl.Z)] rs.DeleteObjects( [FSVUCurve, FSVLCurve, PortCurve, StarboardCurve, FSVMeanCurve]) return FuselageOMLSurf, SternPoint
def transonic_airliner( Propulsion=1, # 1 - twin, 2 - quad EngineDia=2.9, # Diameter of engine intake highlight FuselageScaling=[55.902, 55.902, 55.902], # [x,y,z] scale factors NoseLengthRatio=0.182, # Proportion of forward tapering section of the fuselage TailLengthRatio=0.293, # Proportion of aft tapering section of the fuselage WingScaleFactor=44.56, WingChordFactor=1.0, Topology=1, # Topology = 2 will yield a box wing airliner - use with caution, this is just for demo purposes. SpanStation1=0.31, # Inboard engine at this span station SpanStation2=0.625, # Outboard engine at this span station (ignored if Propulsion=1) EngineCtrBelowLE=0.3558, # Engine below leading edge, normalised by the length of the nacelle - range: [0.35,0.5] EngineCtrFwdOfLE=0.9837, # Engine forward of leading edge, normalised by the length of the nacelle - range: [0.85,1.5] Scarf_deg=3): # Engine scarf angle # Build fuselage geometry rs.EnableRedraw(False) try: FuselageOMLSurf, SternPoint = fuselage_oml.FuselageOML( NoseLengthRatio, TailLengthRatio, Scaling=FuselageScaling, NoseCoordinates=[0, 0, 0], CylindricalMidSection=False, SimplificationReqd=False) except: print "Fuselage fitting failed - stopping." return FuselageHeight = FuselageScaling[2] * 0.105 FuselageLength = FuselageScaling[0] FuselageWidth = FuselageScaling[1] * 0.106 rs.Redraw() if FuselageOMLSurf is None: print "Failed to fit fuselage surface, stopping." return FSurf = rs.CopyObject(FuselageOMLSurf) # Position of the apex of the wing if FuselageHeight < 8.0: WingApex = [0.1748 * FuselageLength, 0, -0.0523 * FuselageHeight] #787:[9.77,0,-0.307] else: WingApex = [0.1748 * FuselageLength, 0, -0.1 * FuselageHeight] #787:[9.77,0,-0.307] # Set up the wing object, including the list of user-defined functions that # describe the spanwise variations of sweep, dihedral, etc. LooseSurf = 1 if Topology == 1: SegmentNo = 10 Wing = liftingsurface.LiftingSurface(WingApex, ta.mySweepAngleFunctionAirliner, ta.myDihedralFunctionAirliner, ta.myTwistFunctionAirliner, ta.myChordFunctionAirliner, ta.myAirfoilFunctionAirliner, LooseSurf, SegmentNo, TipRequired=True) elif Topology == 2: SegmentNo = 101 Wing = liftingsurface.LiftingSurface(WingApex, ta.mySweepAngleFunctionAirliner, bw.myDihedralFunctionBoxWing, ta.myTwistFunctionAirliner, ta.myChordFunctionAirliner, ta.myAirfoilFunctionAirliner, LooseSurf, SegmentNo, TipRequired=True) # Instantiate the wing object and add it to the document rs.EnableRedraw(False) WingSurf, ActualSemiSpan, LSP_area, RootChord, AR, WingTip = Wing.GenerateLiftingSurface( WingChordFactor, WingScaleFactor) rs.Redraw() if Topology == 1: # Add wing to body fairing WTBFXCentre = WingApex[ 0] + RootChord / 2.0 + RootChord * 0.1297 # 787: 23.8 if FuselageHeight < 8.0: WTBFZ = RootChord * 0.009 #787: 0.2 WTBFheight = 0.1212 * RootChord #787:2.7 WTBFwidth = 1.08 * FuselageWidth else: WTBFZ = WingApex[2] + 0.005 * RootChord WTBFheight = 0.09 * RootChord WTBFwidth = 1.15 * FuselageWidth WTBFlength = 1.167 * RootChord #787:26 WTBFXStern = WTBFXCentre + WTBFlength / 2.0 CommS = "_Ellipsoid %3.2f,0,%3.2f %3.2f,0,%3.2f %3.2f,%3.2f,%3.2f %3.2f,0,%3.2f " % ( WTBFXCentre, WTBFZ, WTBFXStern, WTBFZ, 0.5 * (WTBFXCentre + WTBFXStern), 0.5 * WTBFwidth, WTBFZ, 0.5 * (WTBFXCentre + WTBFXStern), WTBFheight) rs.EnableRedraw(False) rs.CurrentView("Perspective") rs.Command(CommS) LO = rs.LastCreatedObjects() WTBF = LO[0] rs.Redraw() # Trim wing inboard section CutCirc = rs.AddCircle3Pt((0, WTBFwidth / 4, -45), (0, WTBFwidth / 4, 45), (90, WTBFwidth / 4, 0)) CutCircDisk = rs.AddPlanarSrf(CutCirc) CutDisk = CutCircDisk[0] rs.ReverseSurface(CutDisk, 1) rs.TrimBrep(WingSurf, CutDisk) elif Topology == 2: # Overlapping wing tips CutCirc = rs.AddCircle3Pt((0, 0, -45), (0, 0, 45), (90, 0, 0)) CutCircDisk = rs.AddPlanarSrf(CutCirc) CutDisk = CutCircDisk[0] rs.ReverseSurface(CutDisk, 1) rs.TrimBrep(WingSurf, CutDisk) # Engine installation (nacelle and pylon) if Propulsion == 1: # Twin, wing mounted SpanStation = SpanStation1 NacelleLength = 1.95 * EngineDia rs.EnableRedraw(False) EngineSection, Chord = act.CutSect(WingSurf, SpanStation) CEP = rs.CurveEndPoint(Chord) EngineStbd, PylonStbd = engine.TurbofanNacelle( EngineSection, Chord, CentreLocation=[ CEP.X - EngineCtrFwdOfLE * NacelleLength, CEP.Y, CEP.Z - EngineCtrBelowLE * NacelleLength ], ScarfAngle=Scarf_deg, HighlightRadius=EngineDia / 2.0, MeanNacelleLength=NacelleLength) rs.Redraw() elif Propulsion == 2: # Quad, wing-mounted NacelleLength = 1.95 * EngineDia rs.EnableRedraw(False) EngineSection, Chord = act.CutSect(WingSurf, SpanStation1) CEP = rs.CurveEndPoint(Chord) EngineStbd1, PylonStbd1 = engine.TurbofanNacelle( EngineSection, Chord, CentreLocation=[ CEP.X - EngineCtrFwdOfLE * NacelleLength, CEP.Y, CEP.Z - EngineCtrBelowLE * NacelleLength ], ScarfAngle=Scarf_deg, HighlightRadius=EngineDia / 2.0, MeanNacelleLength=NacelleLength) rs.DeleteObjects([EngineSection, Chord]) EngineSection, Chord = act.CutSect(WingSurf, SpanStation2) CEP = rs.CurveEndPoint(Chord) EngineStbd2, PylonStbd2 = engine.TurbofanNacelle( EngineSection, Chord, CentreLocation=[ CEP.X - EngineCtrFwdOfLE * NacelleLength, CEP.Y, CEP.Z - EngineCtrBelowLE * NacelleLength ], ScarfAngle=Scarf_deg, HighlightRadius=EngineDia / 2.0, MeanNacelleLength=NacelleLength) rs.Redraw() # Script for generating and positioning the fin rs.EnableRedraw(False) # Position of the apex of the fin P = [0.6524 * FuselageLength, 0.003, FuselageHeight * 0.384] #P = [36.47,0.003,2.254]55.902 RotVec = rs.VectorCreate([1, 0, 0], [0, 0, 0]) LooseSurf = 1 SegmentNo = 200 Fin = liftingsurface.LiftingSurface(P, tail.mySweepAngleFunctionFin, tail.myDihedralFunctionFin, tail.myTwistFunctionFin, tail.myChordFunctionFin, tail.myAirfoilFunctionFin, LooseSurf, SegmentNo) ChordFactor = 1.01 #787:1.01 if Topology == 1: ScaleFactor = WingScaleFactor / 2.032 #787:21.93 elif Topology == 2: ScaleFactor = WingScaleFactor / 3.5 FinSurf, FinActualSemiSpan, FinArea, FinRootChord, FinAR, FinTip = Fin.GenerateLiftingSurface( ChordFactor, ScaleFactor) FinSurf = rs.RotateObject(FinSurf, P, 90, axis=RotVec) FinTip = rs.RotateObject(FinTip, P, 90, axis=RotVec) if Topology == 1: # Tailplane P = [0.7692 * FuselageLength, 0.000, FuselageHeight * 0.29] RotVec = rs.VectorCreate([1, 0, 0], [0, 0, 0]) LooseSurf = 1 SegmentNo = 100 TP = liftingsurface.LiftingSurface(P, tail.mySweepAngleFunctionTP, tail.myDihedralFunctionTP, tail.myTwistFunctionTP, tail.myChordFunctionTP, tail.myAirfoilFunctionTP, LooseSurf, SegmentNo) ChordFactor = 1.01 ScaleFactor = 0.388 * WingScaleFactor #787:17.3 TPSurf, TPActualSemiSpan, TPArea, TPRootChord, TPAR, TPTip = TP.GenerateLiftingSurface( ChordFactor, ScaleFactor) rs.EnableRedraw(True) rs.DeleteObjects([EngineSection, Chord]) try: rs.DeleteObjects([CutCirc]) except: pass try: rs.DeleteObjects([CutCircDisk]) except: pass # Windows # Cockpit windows: rs.EnableRedraw(False) CockpitWindowTop = 0.305 * FuselageHeight CWC1s, CWC2s, CWC3s, CWC4s = fuselage_oml.CockpitWindowContours( Height=CockpitWindowTop, Depth=6) FuselageOMLSurf, Win1 = rs.SplitBrep(FuselageOMLSurf, CWC1s, delete_input=True) FuselageOMLSurf, Win2 = rs.SplitBrep(FuselageOMLSurf, CWC2s, delete_input=True) FuselageOMLSurf, Win3 = rs.SplitBrep(FuselageOMLSurf, CWC3s, delete_input=True) FuselageOMLSurf, Win4 = rs.SplitBrep(FuselageOMLSurf, CWC4s, delete_input=True) rs.DeleteObjects([CWC1s, CWC2s, CWC3s, CWC4s]) (Xmin, Ymin, Zmin, Xmax, Ymax, Zmax) = act.ObjectsExtents([Win1, Win2, Win3, Win4]) CockpitBulkheadX = Xmax CockpitWallPlane = rs.PlaneFromPoints([CockpitBulkheadX, -15, -15], [CockpitBulkheadX, 15, -15], [CockpitBulkheadX, -15, 15]) CockpitWall = rs.AddPlaneSurface(CockpitWallPlane, 30, 30) if 'WTBF' in locals(): rs.TrimBrep(WTBF, CockpitWall) rs.DeleteObject(CockpitWall) # Window lines WIN = [1] NOWIN = [0] # A typical window pattern (including emergency exit windows) WinVec = WIN + 2 * NOWIN + 9 * WIN + 3 * NOWIN + WIN + NOWIN + 24 * WIN + 2 * NOWIN + WIN + NOWIN + 14 * WIN + 2 * NOWIN + WIN + 20 * WIN + 2 * NOWIN + WIN + NOWIN + 20 * WIN if FuselageHeight < 8.0: # Single deck WindowLineHeight = 0.3555 * FuselageHeight WinX = 0.1157 * FuselageLength WindowPitch = 0.609 WinInd = -1 while WinX < 0.75 * FuselageLength: WinInd = WinInd + 1 if WinVec[WinInd] == 1 and WinX > CockpitBulkheadX: WinStbd, WinPort, FuselageOMLSurf = fuselage_oml.MakeWindow( FuselageOMLSurf, WinX, WindowLineHeight) act.AssignMaterial(WinStbd, "Plexiglass") act.AssignMaterial(WinPort, "Plexiglass") WinX = WinX + WindowPitch else: # Fuselage big enough to accommodate two decks # Lower deck WindowLineHeight = 0.17 * FuselageHeight #0.166 WinX = 0.1 * FuselageLength #0.112 WindowPitch = 0.609 WinInd = 0 while WinX < 0.757 * FuselageLength: WinInd = WinInd + 1 if WinVec[WinInd] == 1 and WinX > CockpitBulkheadX: WinStbd, WinPort, FuselageOMLSurf = fuselage_oml.MakeWindow( FuselageOMLSurf, WinX, WindowLineHeight) act.AssignMaterial(WinStbd, "Plexiglass") act.AssignMaterial(WinPort, "Plexiglass") WinX = WinX + WindowPitch # Upper deck WindowLineHeight = 0.49 * FuselageHeight WinX = 0.174 * FuselageLength #0.184 WinInd = 0 while WinX < 0.757 * FuselageLength: WinInd = WinInd + 1 if WinVec[WinInd] == 1 and WinX > CockpitBulkheadX: WinStbd, WinPort, FuselageOMLSurf = fuselage_oml.MakeWindow( FuselageOMLSurf, WinX, WindowLineHeight) act.AssignMaterial(WinStbd, "Plexiglass") act.AssignMaterial(WinPort, "Plexiglass") WinX = WinX + WindowPitch rs.Redraw() act.AssignMaterial(FuselageOMLSurf, "White_composite_external") act.AssignMaterial(WingSurf, "White_composite_external") try: act.AssignMaterial(TPSurf, "ShinyBARedMetal") except: pass act.AssignMaterial(FinSurf, "ShinyBARedMetal") act.AssignMaterial(Win1, "Plexiglass") act.AssignMaterial(Win2, "Plexiglass") act.AssignMaterial(Win3, "Plexiglass") act.AssignMaterial(Win4, "Plexiglass") # Mirror the geometry as required act.MirrorObjectXZ(WingSurf) act.MirrorObjectXZ(WingTip) try: act.MirrorObjectXZ(TPSurf) act.MirrorObjectXZ(TPTip) except: pass if Propulsion == 1: for ObjId in EngineStbd: act.MirrorObjectXZ(ObjId) act.MirrorObjectXZ(PylonStbd) elif Propulsion == 2: for ObjId in EngineStbd1: act.MirrorObjectXZ(ObjId) act.MirrorObjectXZ(PylonStbd1) for ObjId in EngineStbd2: act.MirrorObjectXZ(ObjId) act.MirrorObjectXZ(PylonStbd2) rs.DeleteObject(FSurf) rs.Redraw()
def _BuildLS(self, ChordFactor, ScaleFactor): # Generates a tentative lifting surface, given the general, nondimensio- # nal parameters of the object (variations of chord length, dihedral, etc.) # and the two scaling factors. LEPoints = self._GenerateLeadingEdge() Sections = [] ProjectedSections = [] TEPoints_u = [] TEPoints_l = [] for i, LEP in enumerate(LEPoints): Eps = float(i)/self.SegmentNo Airfoil, Chrd = self.AirfoilFunct(Eps, LEP, self.ChordFunct, ChordFactor, self.DihedralFunct, self.TwistFunct) list.append(Sections, Airfoil) Pr = rs.ProjectCurveToSurface(Chrd,self.XoY_Plane,self.ProjVectorZ) list.append(ProjectedSections, Pr) list.append(TEPoints_l, rs.CurveEndPoint(Airfoil)) list.append(TEPoints_u, rs.CurveStartPoint(Airfoil)) rs.DeleteObjects(Chrd) LS = rs.AddLoftSrf(Sections,loft_type=self.LooseSurf) if LS==None: # Failed to fit loft surface. Try another fitting algorithm TECurve_u = rs.AddInterpCurve(TEPoints_u) TECurve_l = rs.AddInterpCurve(TEPoints_l) rails = [] list.append(rails, TECurve_u) list.append(rails, TECurve_l) # Are the first and last curves identical? # AddSweep fails if they are, so if that is the case, one is skipped CDev = rs.CurveDeviation(Sections[0],Sections[-1]) if CDev==None: shapes = Sections LS = rs.AddSweep2(rails, shapes, False) else: shapes = Sections[:-1] LS = rs.AddSweep2(rails, shapes, True) rs.DeleteObjects(rails) rs.DeleteObjects([TECurve_u, TECurve_l]) WingTip = None if self.TipRequired: TipCurve = Sections[-1] TipCurve = act.AddTEtoOpenAirfoil(TipCurve) WingTip = rs.AddPlanarSrf(TipCurve) rs.DeleteObject(TipCurve) # Calculate projected area # In some cases the projected sections cannot all be lofted in one go # (it happens when parts of the wing fold back onto themselves), so # we loft them section by section and we compute the area as a sum. LSP_area = 0 # Attempt to compute a projected area try: for i, LEP in enumerate(ProjectedSections): if i < len(ProjectedSections)-1: LSPsegment = rs.AddLoftSrf(ProjectedSections[i:i+2]) SA = rs.SurfaceArea(LSPsegment) rs.DeleteObject(LSPsegment) LSP_area = LSP_area + SA[0] except: print "Failed to compute projected area. Using half of surface area instead." LS_area = rs.SurfaceArea(LS) LSP_area = 0.5*LS_area[0] BB = rs.BoundingBox(LS) if BB: ActualSemiSpan = BB[2].Y - BB[0].Y else: ActualSemiSpan = 0.0 # Garbage collection rs.DeleteObjects(Sections) try: rs.DeleteObjects(ProjectedSections) except: print "Cleanup: no projected sections to delete" rs.DeleteObjects(LEPoints) # Scaling Origin = rs.AddPoint([0,0,0]) ScaleXYZ = (ScaleFactor, ScaleFactor, ScaleFactor) LS = rs.ScaleObject(LS, Origin, ScaleXYZ) if self.TipRequired and WingTip: WingTip = rs.ScaleObject(WingTip, Origin, ScaleXYZ) rs.DeleteObject(Origin) ActualSemiSpan = ActualSemiSpan*ScaleFactor LSP_area = LSP_area*ScaleFactor**2.0 RootChord = (self.ChordFunct(0)*ChordFactor)*ScaleFactor AR = ((2.0*ActualSemiSpan)**2.0)/(2*LSP_area) return LS, ActualSemiSpan, LSP_area, RootChord, AR, WingTip