def test_fuel_value_734(self): nn = 5 p = om.Problem() tas = np.linspace(0, 100, nn) tas_kt = tas * 1.94384 alt = 0 ivc = p.model.add_subsystem('ivc', subsys=om.IndepVarComp(), promotes_outputs=['*']) ivc.add_output(name='tas', val=tas, units='m/s') ivc.add_output(name='alt', val=alt, units='m') p.model.connect('alt', ['atmos.h', 'propulsion.elevation']) p.model.connect('tas', ['propulsion.tas']) p.model.add_subsystem(name='atmos', subsys=USatm1976Comp(num_nodes=1)) p.model.add_subsystem(name='propulsion', subsys=PropulsionGroup( num_nodes=nn, airplane=self.airplane_734)) p.model.connect('atmos.sos', 'propulsion.sos') p.model.connect('atmos.pres', ['propulsion.p_amb']) p.setup() p.run_model() ff = FuelFlow(ac='B734') reference_ff = [-1 * ff.takeoff(tas=v, alt=alt) for v in tas_kt] assert_near_equal(p.get_val('propulsion.m_dot'), reference_ff, tolerance=0.001)
def __init__(self, aircraft_name, write_output: bool = False): self.write = write_output # General Variables self.aircraft_data = prop.aircraft(aircraft_name) self.dt = 1.0 / 60.0 # simulation timestep 60 per seconds aircraft_txt = open(f"./data/{aircraft_name}.json", 'r').read() self.aircraft = json.loads(aircraft_txt) eng_name = self.aircraft_data["engine"]["default"] self.ac_thrust = Thrust(ac=self.aircraft["Name"], eng=eng_name) # self.ac_thrust = Thrust(f"./data/{aircraft_name}_thrust.csv") # Performance Variables (all unit in SI except stated otherwise) self.lift_drag = LiftDrag(f"./data/{aircraft_name}_ld.csv") self.g = 9.81 self.mass = self.aircraft_data["limits"]["MTOW"] self.thrust_lever = 1.0 self.altitude = 0.0 self.pressure = 0.0 self.density = 0.0 self.temp = 0.0 self.cas = 0.0 self.tas = 0.0 self.v_y = 0.0 # vertical Speed self.vs = 0.0 # Vertical Speed [fpm] self.drag = 0.0 self.thrust = 0.0 self.lift = 0.0 self.weight = 0.0 self.t_d = 0.0 # thrust minus drag aka exceed thrust self.l_w = 0.0 # lift minus weight aka exceed lift self.pitch = 0.0 self.fpa = 0.0 self.aoa = 0.0 self.Q = 0.0 # tas² * density self.acc_x = 0.0 self.acc_y = 0.0 self.distance_x = 0.0 # total distance[m] self.d_x = 0.0 # instantaneous X distance[m] self.d_y = 0.0 # instantaneous Y distance[m] self.phase = 0 # Current phase self.cd = 0.0 self.cl = 0.0 self.drag0 = self.aircraft["Drag0"] self.lift0 = self.aircraft["Lift0"] self.gear = False self.flaps = 0 self.pitch_target = 0.0 self.pitch_rate_of_change = 3.0 # rate of change of the pitch [°/sec] self.ac_fuelflow = FuelFlow(ac=self.aircraft["Name"], eng=eng_name) if self.write: self.output = open("output.csv", 'w+') self.output.write(self.__get_header()) self.output.flush()
def __init__(self, ac): super(CruiseOptimizer, self).__init__() self.ac = ac self.aircraft = prop.aircraft(ac) self.thrust = Thrust(ac) self.fuelflow = FuelFlow(ac) self.drag = Drag(ac) # parameters to be optimized: # Mach number, altitude self.x0 = np.array([0.3, 25000 * aero.ft]) self.normfactor = calc_normfactor(self.x0) self.bounds = None self.update_bounds()
class CruiseOptimizer(object): """Optimizer for cursie mach number and altitude.""" def __init__(self, ac): super(CruiseOptimizer, self).__init__() self.ac = ac self.aircraft = prop.aircraft(ac) self.thrust = Thrust(ac) self.fuelflow = FuelFlow(ac) self.drag = Drag(ac) # parameters to be optimized: # Mach number, altitude self.x0 = np.array([0.3, 25000 * aero.ft]) self.normfactor = calc_normfactor(self.x0) self.bounds = None self.update_bounds() def update_bounds(self, **kwargs): machmin = kwargs.get('machmin', 0.5) machmax = kwargs.get('machmax', self.aircraft['limits']['MMO']) hmin = kwargs.get('hmin', 25000 * aero.ft) hmax = kwargs.get('hmax', self.aircraft['limits']['ceiling']) self.bounds = np.array([[machmin, machmax], [hmin, hmax] ]) * self.normfactor.reshape(2, -1) def func_fuel(self, x, mass): mach, h = denormalize(x, self.normfactor) va = aero.mach2tas(mach, h) ff = self.fuelflow.enroute(mass, va / aero.kts, h / aero.ft) ff_m = ff / (va + 1e-3) * 1000 # print("%.03f" % mach, "%d" % (h/aero.ft), "%.05f" % ff_m) return ff_m def func_time(self, x, mass): mach, h = denormalize(x, self.normfactor) va = aero.mach2tas(mach, h) va_inv = 1 / (va + 1e-4) * 1000 # print("%.03f" % mach, "%d" % (h/aero.ft), "%.02f" % va) return va_inv def func_cons_lift(self, x, mass): mach, h = denormalize(x, self.normfactor) va = aero.mach2tas(mach, h) Tmax = self.thrust.cruise(va / aero.kts, h / aero.ft) qS = 0.5 * aero.density(h) * va**2 * self.aircraft['wing']['area'] cd0 = self.drag.polar['clean']['cd0'] k = self.drag.polar['clean']['k'] mach_crit = self.drag.polar['mach_crit'] if mach > mach_crit: cd0 += 20 * (mach - mach_crit)**4 dL2 = qS**2 * (1 / k * (Tmax / (qS + 1e-3) - cd0)) - (mass * aero.g0)**2 return dL2 def func_cons_thrust(self, x, mass): mach, h = denormalize(x, self.normfactor) va = aero.mach2tas(mach, h) D = self.drag.clean(mass, va / aero.kts, h / aero.ft) Tmax = self.thrust.cruise(va / aero.kts, h / aero.ft) dT = Tmax - D return dT def optimize(self, goal, mass): if goal == 'fuel': func = self.func_fuel elif goal == 'time': func = self.func_time else: raise RuntimeError('Optimization goal [%s] not avaiable.' % goal) x0 = self.x0 * self.normfactor res = minimize( func, x0, args=(mass, ), bounds=self.bounds, jac=lambda x, m: Jacobian(lambda x: func(x, m))(x), options={'maxiter': 200}, constraints=( { 'type': 'ineq', 'args': (mass, ), 'fun': lambda x, m: self.func_cons_thrust(x, m), 'jac': lambda x, m: Jacobian(lambda x: self.func_cons_thrust( x, m))(x) }, { 'type': 'ineq', 'args': (mass, ), 'fun': lambda x, m: self.func_cons_lift(x, m), 'jac': lambda x, m: Jacobian(lambda x: self.func_cons_lift(x, m)) (x) }, )) return res
class Performance(): """Calulate Instantaneous performance of one object""" def __init__(self, aircraft_name, write_output: bool = False): self.write = write_output # General Variables self.aircraft_data = prop.aircraft(aircraft_name) self.dt = 1.0 / 60.0 # simulation timestep 60 per seconds aircraft_txt = open(f"./data/{aircraft_name}.json", 'r').read() self.aircraft = json.loads(aircraft_txt) eng_name = self.aircraft_data["engine"]["default"] self.ac_thrust = Thrust(ac=self.aircraft["Name"], eng=eng_name) # self.ac_thrust = Thrust(f"./data/{aircraft_name}_thrust.csv") # Performance Variables (all unit in SI except stated otherwise) self.lift_drag = LiftDrag(f"./data/{aircraft_name}_ld.csv") self.g = 9.81 self.mass = self.aircraft_data["limits"]["MTOW"] self.thrust_lever = 1.0 self.altitude = 0.0 self.pressure = 0.0 self.density = 0.0 self.temp = 0.0 self.cas = 0.0 self.tas = 0.0 self.v_y = 0.0 # vertical Speed self.vs = 0.0 # Vertical Speed [fpm] self.drag = 0.0 self.thrust = 0.0 self.lift = 0.0 self.weight = 0.0 self.t_d = 0.0 # thrust minus drag aka exceed thrust self.l_w = 0.0 # lift minus weight aka exceed lift self.pitch = 0.0 self.fpa = 0.0 self.aoa = 0.0 self.Q = 0.0 # tas² * density self.acc_x = 0.0 self.acc_y = 0.0 self.distance_x = 0.0 # total distance[m] self.d_x = 0.0 # instantaneous X distance[m] self.d_y = 0.0 # instantaneous Y distance[m] self.phase = 0 # Current phase self.cd = 0.0 self.cl = 0.0 self.drag0 = self.aircraft["Drag0"] self.lift0 = self.aircraft["Lift0"] self.gear = False self.flaps = 0 self.pitch_target = 0.0 self.pitch_rate_of_change = 3.0 # rate of change of the pitch [°/sec] self.ac_fuelflow = FuelFlow(ac=self.aircraft["Name"], eng=eng_name) if self.write: self.output = open("output.csv", 'w+') self.output.write(self.__get_header()) self.output.flush() def __get_Q(self): self.pressure, self.density, self.temp = aero.atmos(self.altitude) self.Q = self.density * (self.tas**2) def __calculate_FPA(self): self.fpa = math.degrees(math.atan2(self.d_y, self.d_x)) self.aoa = self.pitch - self.fpa def __calculate_lift(self): if self.gear: self.cl += self.aircraft["Gear"]["Lift"] if self.flaps > 0: self.cl += self.aircraft["Flaps"][self.flaps - 1]["Lift"] self.lift = self.lift0\ + (0.5 * self.Q * self.aircraft["WingSpan"] * self.cl) self.lift *= 1.5 def __calculate_drag(self, new: bool = True): if self.gear: self.cd += self.aircraft["Gear"]["Drag"] if self.flaps > 0: self.cd += self.aircraft["Flaps"][self.flaps - 1]["Drag"] self.drag = self.drag0\ + (0.5 * self.Q * self.aircraft["WingSpan"] * self.cd) def __change_pitch(self) -> None: """ Change pitch in relation of pitch target change of pitch occure with a 3 degrees per seconds change. """ if self.pitch == self.pitch_target: return if self.pitch > self.pitch_target: self.pitch -= self.pitch_rate_of_change * self.dt elif self.pitch < self.pitch_target: self.pitch += self.pitch_rate_of_change * self.dt def run(self) -> bool: """Calculate aircraft performance till thrust reduction altitude Args: target_alt (float, optional): The thrust reduction altitude in meters. Defaults to 457.2m (1500.0 feets) Returns: bool: True if the phase is still valid, else False """ if self.distance_x == 0: self.aoa = self.pitch self.cl, self.cd = self.lift_drag.get_data(self.aoa) self.__get_Q() self.__change_pitch() self.__calculate_drag() self.__calculate_lift() self.g = local_gravity(50.0, self.altitude) self.weight = self.mass * self.g max_thrust = self.ac_thrust.takeoff(alt=self.altitude / aero.ft, tas=self.tas / aero.kts) idle_thrust = self.ac_thrust.descent_idle(tas=self.tas / aero.kts, alt=self.altitude / aero.ft) self.thrust = interpolate(self.thrust_lever, 0.0, 1.0, idle_thrust, max_thrust) fuelflow = self.ac_fuelflow.at_thrust(acthr=self.thrust / 2.0, alt=self.altitude / aero.ft) self.mass -= fuelflow * self.dt * 2 self.t_d = self.thrust - self.drag\ - (self.weight * math.sin(math.radians(self.pitch))) self.l_w = self.lift\ - (self.weight * math.cos(math.radians(self.pitch)))\ + (self.thrust * math.sin(math.radians(self.pitch))) acc = self.t_d / self.mass self.acc_x = acc * math.cos(math.radians(self.pitch)) self.acc_y = acc * math.sin(math.radians(self.pitch)) v_acc = self.l_w / self.mass self.acc_y += v_acc * math.cos(math.radians(self.pitch)) self.acc_x += v_acc * math.sin(math.radians(self.pitch)) self.d_x = (self.tas * self.dt) + 0.5 * self.acc_x * (self.dt**2) self.d_y = (self.v_y * self.dt) + 0.5 * self.acc_y * (self.dt**2) self.tas += self.acc_x * self.dt self.cas = aero.tas2cas(self.tas, self.altitude) self.v_y += self.acc_y * self.dt if self.altitude <= 0: self.altitude = 0 if self.d_y < 0: self.d_y = 0 if self.v_y < 0: self.v_y = 0 self.vs = 0 self.altitude += self.d_y self.distance_x += self.d_x self.vs = self.v_y / aero.fpm self.__calculate_FPA() if self.write: self.output.write(str(self)) self.output.flush() def __str__(self): return f"{self.mass},{self.altitude},{self.pressure},{self.density},"\ f"{self.temp},{self.cas},{self.tas},{self.v_y},{self.vs},"\ f"{self.drag},{self.thrust},{self.t_d},{self.pitch},"\ f"{self.fpa},{self.aoa},{self.Q},{self.acc_x},{self.acc_y},"\ f"{self.distance_x},{self.d_x},{self.d_y},{self.phase},"\ f"{self.cd},{self.drag0},{self.gear},{self.flaps},{self.cl},"\ f"{self.lift},{self.weight},{self.l_w},{self.thrust_lever},"\ f"{self.altitude / aero.ft},{self.g},{self.pitch_target}\n" def __get_header(self): return "Mass,Altitude,Pressure,Density,Temperature,Cas,Tas,Vy,VS,"\ "Drag,Thrust,T-D,Pitch,FPA,AOA,Q,AccelerationX,AccelerationY,"\ "DistanceX,Dx,Dy,Phase,Cd,Cd0,Gear,Flaps,Cl,Lift,Weight,L-W,"\ "Thrust Limit,Altitude FT,Gravity,Target Pitch\n"
import numpy as np import matplotlib.pyplot as plt from openap import Emission, FuelFlow, prop from mpl_toolkits.mplot3d import Axes3D ac = "A320" aircraft = prop.aircraft(ac) fuelflow = FuelFlow(ac=ac) emission = Emission(ac=ac) tas = np.linspace(50, 500, 50) alt = np.linspace(100, 35000, 50) tas_, alt_ = np.meshgrid(tas, alt) mass = aircraft["limits"]["MTOW"] * 0.85 ff = fuelflow.enroute(mass=mass, tas=tas_, alt=alt_, path_angle=0) co2 = emission.co2(ff) h2o = emission.h2o(ff) nox = emission.nox(ff, tas=tas_, alt=alt_) co = emission.co(ff, tas=tas_, alt=alt_) hc = emission.hc(ff, tas=tas_, alt=alt_) fig = plt.figure() ax = fig.gca(projection="3d") surf = ax.plot_surface(tas_, alt_, ff) plt.title("fuel flow (kg/s)") plt.xlabel("TAS (kt)") plt.ylabel("Altitude (ft)") plt.show()
from openap import FuelFlow fuel = FuelFlow(ac='A320', eng='CFM56-5B4') print('-'*70) FF = fuel.at_thrust(acthr=50000, alt=0) print("fuel.at_thrust(acthr=50000, alt=0)") print(FF) print('-'*70) FF = fuel.at_thrust(acthr=50000, alt=20000) print("fuel.at_thrust(acthr=50000, alt=20000)") print(FF) print('-'*70) FF = fuel.takeoff(tas=100, alt=0, throttle=1) print("fuel.takeoff(tas=100, alt=0, throttle=1)") print(FF) print('-'*70) FF = fuel.enroute(mass=60000, tas=200, alt=20000, path_angle=3) print("fuel.enroute(mass=60000, tas=200, alt=20000, path_angle=3)") print(FF) print('-'*70) FF = fuel.enroute(mass=60000, tas=230, alt=32000, path_angle=0) print("fuel.enroute(mass=60000, tas=230, alt=32000, path_angle=0)") print(FF) print('-'*70) FF = fuel.enroute(mass=[60000], tas=[230], alt=[32000], path_angle=[0])
from openap import prop, FuelFlow, Emission, WRAP available_acs = prop.available_aircraft(use_synonym=True) for actype in available_acs: # print(actype) aircraft = prop.aircraft(ac=actype, use_synonym=True) wrap = WRAP(ac=actype, use_synonym=True) fuelflow = FuelFlow(ac=actype, use_synonym=True) emission = Emission(ac=actype, use_synonym=True) available_acs = prop.available_aircraft(use_synonym=False) for actype in available_acs: # print(actype) aircraft = prop.aircraft(ac=actype, use_synonym=False) wrap = WRAP(ac=actype, use_synonym=True) fuelflow = FuelFlow(ac=actype, use_synonym=True) emission = Emission(ac=actype, use_synonym=True)