def DropandHover(): global s PP = GetParams()[1] s = simulate(500) initialHeight = 500 s.y0 = [0, initialHeight, 0, 0] s.x2[s.n] = initialHeight s.pitch[s.n] = 90 s.pitchShape = 'const' s.pitchCeiling = 90 # make the aircraft mass lower because the powerplant is not enough to # make the full aircraft hover s.totalMass = 4 weight = s.totalMass * AC['g'] s.TShape = 'sigmoid' s.TFloor = 0 s.TCeiling = weight s.TStart = 0 s.TEnd = 50 s.runtime = 200 s.steps = 200 s.RunInterval() Plot(s, 'thrust', 'pitch', 'x2', 'v2', save=True, title=False, direct='simulationPics/hover/') Fig, ax1 = plt.subplots() ax2 = ax1.twinx() ax1.plot(s.timeArray[:s.n], s.x2[:s.n], label='Height') ax2.plot(s.timeArray[:s.n], s.v2[:s.n], 'r', label='V2') ax1.yaxis.set_major_formatter(StrMethodFormatter('{x:,.0f}')) lines, labels = ax1.get_legend_handles_labels() lines2, labels2 = ax2.get_legend_handles_labels() ax1.set_xlabel('time (seconds)') ax1.set_ylabel('AGL (m)') ax2.set_ylabel('velocity (m/s)') ax2.legend(lines + lines2, labels + labels2, loc=0) plt.tight_layout() plt.savefig('simulationPics/hover/x2v2.png') plt.show()
def __init__(self, rng=50, plant=None): self.xHist = np.zeros((4, 1)) self.cHist = np.zeros(1) # if plant is not given, use standard unsized plant if not plant: self.plant = GetParams()[1] else: self.plant = plant self.rng = rng self.iterations = 0 self.result = None self.x0 = np.array([10, 2.9, 5, 25])
def Fly(): global sim, fly _, PP = GetParams() # initialize classes sim = simulate(700, PP) # take off TakeOff(sim) Climb(sim, v1=13.66, desiredCR=2, desiredHeight=410) Cruise(sim, v1=16.9, distance=500) Glide(sim, 0) sim.CalculateE()
def PlotPlantSizing(): """ Plots the power plant mass (ICE mass + EM mass) produced when sizing at different rEM. rEM = maximum EM power / total power demand as given by ADRpy Returns ------- None. """ _, PP = GetParams() rEMArray = np.linspace(0, 1, 11) massArray = np.zeros(11) for index, i in enumerate(rEMArray): sizedP = PlantSizing(PP, i) m = sizedP['EMMass'] + sizedP['ICEMass'] massArray[index] = m plt.plot(rEMArray, massArray) plt.xlabel('Ratio of power produced by EM') plt.ylabel('ICE + EM mass (kg)') plt.title('Plant mass at different ICE and EM sizes')
def __init__(self, rng, plant=None, runSizing=True): self.plotMarkers = np.array([]) self.rng = rng * 1000 self.runtime = 0. self.integrator = 'odeint' self.steps = 0 self.time = 0. self.payloadDropped = False if not plant: _, self.PP = GetParams() else: self.PP = plant.copy() # size the powerplant, get updated dictionary to store in object if runSizing: self.PP = PlantSizing(self.PP) self.battChoice = int(self.PP['battChoice']) self.battCap = self.PP['battCapList'][self.battChoice] self.battSE = self.PP['battSEList'][self.battChoice] self.battMass = self.PP['battMassList'][self.battChoice] # zero fuel mass - everything but fuel is included here self.zfm = self.battMass + AC['emptyMass'] + AC['payloadMass'] self.totalMass = AC['emptyMass'] + AC['payloadMass'] + self.PP['fullTankMass']\ + self.battMass + self.PP['EMMass'] + self.PP['ICEMass'] # contains x1, x2, v1, v2 in four columns self.solution = False # vars for sigmoid schedules self.TFloor = 0. self.pitchFloor = 0. self.TCeiling = 0. self.pitchCeiling = 0. self.TStart = 0. self.pitchStart = 0. self.TEnd = 0. self.pitchEnd = 0. self.TShape = 'const' self.pitchShape = 'const' # total amount of data, total time steps returned from ODE solver self.n = 0 # initial values for ode solver self.y0 = np.zeros(4) """ data arrays """ # angles self.alpha = np.zeros(10**3) self.pitch = np.zeros(10**3) self.gamma = np.zeros(10**3) # kinematics self.x1 = np.zeros(10**3) self.x2 = np.zeros(10**3) self.v1 = np.zeros(10**3) self.v2 = np.zeros(10**3) self.a1 = np.zeros(10**3) self.a2 = np.zeros(10**3) # time that corresponds with each index self.timeArray = np.zeros(10**3) # thrust self.thrust = np.zeros(10**3) self.EMThrust = np.zeros(10**3) self.ICEThrust = np.zeros(10**3) # energy related self.totalE = np.zeros(10**3) self.fuelMass = np.zeros(10**3) self.fuelE = np.zeros(10**3) self.SOC = np.zeros(10**3) self.battE = np.zeros(10**3) # power related self.power = np.zeros(10**3) # total power coming out of the prop self.fuelP = np.zeros(10**3) # power consumed by ICE self.battP = np.zeros(10**3) # power consumed by EM self.ICEShaftP = np.zeros(10**3) self.EMShaftP = np.zeros(10**3) self.ICEPropP = np.zeros( 10**3) # total power produced by the ICE connected propeller self.EMPropP = np.zeros( 10**3) # total power produced by the EM connected propeller self.ICEEff = np.zeros(10**3) # ICE brake efficiency ### propeller related ### # -- EM Propeller -- # # -- ICE Propeller -- # self.EMrps = np.zeros(10**3) self.ICErps = np.zeros(10**3) self.EMJ = np.zeros(10**3) self.ICEJ = np.zeros(10**3) self.EMPropEff = np.zeros(10**3) self.ICEPropEff = np.zeros(10**3) self.EMTotEff = np.zeros(10**3) self.ICETotEff = np.zeros(10**3) self.rEM = np.zeros(10**3) # ratio of output power provided by EM # 0 - off, 1 - ICE only, 2 - ICE ideal and EM, 3 - EM max and ICE, 4 - excess self.RBOut = np.zeros(10**3) self.massCost = np.zeros(10**3)
# artificial fix if the J value passed is too far out of the interpolation range if PropCq < 0: PropCq = 0 Q = PropCq * Density(h) * rps**2 * D**5 return Q def PropTFun(v, rps, h): # prevent division by zero when calculating J if rps == 0: return 0 else: J = v / (rps * PP['D']) PropCt = PP['CtFun'](J) # artificial fix if the J value passed is too far out of the interpolation range if PropCt < 0: PropCt = 0 T = PropCt * Density(h) * rps**2 * PP['D']**4 return T AC, PP = GetParams('aircraft_params.txt') PP = PlantSizing(PP)
def Results(rng=50): global op, op1, op2, op3 _, plant0 = GetParams() plant0 = PlantSizing(plant0) # save unchanged simulator for later s0 = simulate(rng, plant0, runSizing=False) ########## Start 1st opt ########## # create a sized plant dictionary _, plant1 = GetParams() plant1 = PlantSizing(plant1) # get prelim results with sized plant op1 = Optimize(rng, plant1) op1.optm() # save the simulator object that contains the last iteration of the optimisation op1.MCostF(op1.result.x, saveSim=True) # amount of fuel actually needed for flight usedFuelM1 = op1.sim.fuelMassCost[op1.sim.n - 1] # necessary batter capacity needed for flight usedBattE1 = op1.sim.battE[op1.sim.n - 1] battChoice1 = 4 for index, i in enumerate(plant1['battCapList']): if i > usedBattE1: battChoice1 = index break print('\nFuel mass used on the first optimisation:', round(usedFuelM1, 3), 'kg') print('Battery energy used on the first optimisation:', round(usedBattE1 / 1000, 3), 'kJ') print('Now attempting to fly with the', s0.PP['battMassList'][battChoice1], 'kg battery\n') ########## End 1st opt ########## ########## Start 2nd opt ########## # create another sized plant dictionary _, plant2 = GetParams() plant2 = PlantSizing(plant2) # update the masses, only change the plant2 dict to keep track of old values plant2['fullTankMass'] = usedFuelM1 plant2['battChoice'] = battChoice1 # get prelim results with sized plant op2 = Optimize(rng, plant2) op2.optm() # save new, lighter profile op2.MCostF(op2.result.x, saveSim=True) # amount of fuel actually needed for flight usedFuelM2 = op2.sim.fuelMassCost[op2.sim.n - 1] # necessary battery capacity needed for flight usedBattE2 = op2.sim.battE[op2.sim.n - 1] # check if the same battery should be used for index, i in enumerate(plant2['battCapList']): if i > usedBattE2: battChoice2 = index break # check how much less fuel is used on second flight fSavings = abs((usedFuelM1 - usedFuelM2) / usedFuelM1) print('\nFuel mass used on the second optimisation:', round(usedFuelM2, 3), 'kg') print('Battery energy used on the second optimisation:', round(usedBattE2 / 1000, 3), 'kJ\n') ########## End Second opt ########## ########## Start 3rd opt ########## # if the fuel savings are siginificant again, or a different battery can be chosen # run the optimisation again if fSavings > 0.1 or battChoice2 != battChoice1: print('Now attempting to fly with the', s0.PP['battMassList'][battChoice2], 'kg battery') _, plant3 = GetParams() plant3 = PlantSizing(plant3) # update the masses, only change the s3 plant dict to keep track of values plant3['fullTankMass'] = usedFuelM2 plant3['battChoice'] = battChoice2 # run optimization again but with smaller battery etc. op3 = Optimize(rng, plant3) op3.optm() # save new profile op3.MCostF(op3.result.x, saveSim=True) battMassSavings = s0.battMass - op3.sim.battMass fuelMassSavings = s0.PP['fullTankMass'] - op3.sim.PP['fullTankMass'] ########## End 3rd opt ########## # if second run was sufficient, should get values for saved mass else: battMassSavings = s0.battMass - op2.sim.battMass fuelMassSavings = s0.PP['fullTankMass'] - op2.sim.PP['fullTankMass'] print('The optimisation terminated. The optimisation saved at least', round(battMassSavings, 2), 'kg in battery mass and ', round(fuelMassSavings, 2), 'kg in fuel which are now available', 'for payload') if 's3' in locals(): return op1, op2, op3 else: return op1, op2