def compute_from(self, start: FlightPoint) -> pd.DataFrame: self.complete_flight_point(start) # needed to ensure all speed values are computed. if self.target.altitude is not None: if isinstance(self.target.altitude, str): # Target altitude will be modified along the process, so we keep track # of the original order in target CL, that is not used otherwise. self.target.CL = self.target.altitude # pylint: disable=invalid-name # let's put a numerical, negative value in self.target.altitude to # ensure there will be no problem in self._get_distance_to_target() self.target.altitude = -1000.0 self.interrupt_if_getting_further_from_target = False else: # Target altitude is fixed, back to original settings (in case # this instance is used more than once) self.target.CL = None self.interrupt_if_getting_further_from_target = True atm = AtmosphereSI(start.altitude) if self.target.equivalent_airspeed == self.CONSTANT_VALUE: atm.equivalent_airspeed = start.equivalent_airspeed start.true_airspeed = atm.true_airspeed elif self.target.mach == self.CONSTANT_VALUE: atm.mach = start.mach start.true_airspeed = atm.true_airspeed return super().compute_from(start)
def _get_distance_to_target(self, flight_points: List[FlightPoint]) -> float: current = flight_points[-1] # Max flight level is first priority max_authorized_altitude = self.maximum_flight_level * 100.0 * foot if current.altitude >= max_authorized_altitude: return max_authorized_altitude - current.altitude if self.target.CL: # Optimal altitude is based on a target Mach number, though target speed # may be specified as TAS or EAS. If so, Mach number has to be computed # for target altitude and speed. # First, as target speed is expected to be set to self.CONSTANT_VALUE for one # parameter. Let's get the real value from start point. target_speed = copy(self.target) for speed_param in ["true_airspeed", "equivalent_airspeed", "mach"]: if isinstance(getattr(target_speed, speed_param), str): setattr(target_speed, speed_param, getattr(flight_points[0], speed_param)) # Now, let's compute target Mach number atm = AtmosphereSI(max(self.target.altitude, current.altitude)) if target_speed.equivalent_airspeed: atm.equivalent_airspeed = target_speed.equivalent_airspeed target_speed.true_airspeed = atm.true_airspeed if target_speed.true_airspeed: atm.true_airspeed = target_speed.true_airspeed target_speed.mach = atm.mach # Now we compute optimal altitude optimal_altitude = self._get_optimal_altitude( current.mass, target_speed.mach, current.altitude ) if self.target.CL == self.OPTIMAL_ALTITUDE: self.target.altitude = optimal_altitude else: # self.target.CL == self.OPTIMAL_FLIGHT_LEVEL: self.target.altitude = get_closest_flight_level( optimal_altitude, up_direction=False ) if self.target.altitude is not None: return self.target.altitude - current.altitude if self.target.true_airspeed and self.target.true_airspeed != self.CONSTANT_VALUE: return self.target.true_airspeed - current.true_airspeed if ( self.target.equivalent_airspeed and self.target.equivalent_airspeed != self.CONSTANT_VALUE ): return self.target.equivalent_airspeed - current.equivalent_airspeed if self.target.mach is not None and self.target.mach != self.CONSTANT_VALUE: return self.target.mach - current.mach raise FastFlightSegmentIncompleteFlightPoint( "No valid target definition for altitude change." )
def distance_to_optimum(altitude): atm = AtmosphereSI(altitude) true_airspeed = mach * atm.speed_of_sound optimal_air_density = (2.0 * mass * g / (self.reference_area * true_airspeed**2 * self.polar.optimal_cl)) return (atm.density - optimal_air_density) * 100.0
def complete_flight_point(self, flight_point: FlightPoint): """ Computes data for provided flight point. Assumes that it is already defined for time, altitude, mass, ground distance and speed (TAS, EAS, or Mach). :param flight_point: the flight point that will be completed in-place """ flight_point.engine_setting = self.engine_setting self._complete_speed_values(flight_point) atm = AtmosphereSI(flight_point.altitude) reference_force = 0.5 * atm.density * flight_point.true_airspeed**2 * self.reference_area if self.polar: flight_point.CL = flight_point.mass * g / reference_force flight_point.CD = self.polar.cd(flight_point.CL) else: flight_point.CL = flight_point.CD = 0.0 flight_point.drag = flight_point.CD * reference_force self._compute_propulsion(flight_point) flight_point.slope_angle, flight_point.acceleration = self._get_gamma_and_acceleration( flight_point.mass, flight_point.drag, flight_point.thrust)
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): if self.options["low_speed_aero"]: mach = inputs["data:aerodynamics:aircraft:takeoff:mach"] altitude = 0.0 else: mach = inputs["data:TLAR:cruise_mach"] altitude = inputs["data:mission:sizing:main_route:cruise:altitude"] atm = AtmosphereSI(altitude) atm.mach = mach reynolds = atm.unitary_reynolds if self.options["low_speed_aero"]: outputs["data:aerodynamics:wing:low_speed:reynolds"] = reynolds else: outputs["data:aerodynamics:wing:cruise:reynolds"] = reynolds
def _complete_speed_values(flight_point: FlightPoint): """ Computes consistent values between TAS, EAS and Mach, assuming one of them is defined. """ atm = AtmosphereSI(flight_point.altitude) if flight_point.true_airspeed is None: if flight_point.mach is not None: atm.mach = flight_point.mach elif flight_point.equivalent_airspeed is not None: atm.equivalent_airspeed = flight_point.equivalent_airspeed else: raise FastFlightSegmentIncompleteFlightPoint( "Flight point should be defined for true_airspeed, " "equivalent_airspeed, or mach.") flight_point.true_airspeed = atm.true_airspeed else: atm.true_airspeed = flight_point.true_airspeed flight_point.mach = atm.mach flight_point.equivalent_airspeed = atm.equivalent_airspeed
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. from stdatm import AtmosphereSI RUBBER_ENGINE_DESCRIPTION = """ Parametric engine model as OpenMDAO component. Implementation of E. Roux models for fuel consumption of low bypass ratio engines For more information, see RubberEngine class in FAST-OAD developer documentation. """ # Atmosphere at limits of troposhere ATM_SEA_LEVEL = AtmosphereSI(0) ATM_TROPOPAUSE = AtmosphereSI(11000) # Constants for computation of maximum thrust --------------------------------- # (see E. Roux model definition in roux:2005) A_MS = -2.74e-4 A_FM = 2.67e-4 B_MS = 1.91e-2 B_FM = -2.35e-2 C_MS = 1.21e-3 C_FM = -1.32e-3 D_MS = -8.48e-4 D_FM = 3.14e-4 E_MS = 8.96e-1 E_FM = 5.22e-1