Source code for flight_mech.plane

"""
Module to compute the plane performances.
"""

###########
# Imports #
###########

# Python imports #

import os
from typing import Literal
import json

# Dependencies #

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors

# Local imports #

from flight_mech.atmosphere import (
    StandardAtmosphere
)
from flight_mech.environment import (
    EarthEnvironment
)

#############
# Constants #
#############

# Define a default plane database location
default_plane_database = os.path.join(
    os.path.dirname(__file__), "plane_database")

###########
# Classes #
###########

[docs] class Plane: """ Model to define a plane and compute its characteristics. """ # Mass m_empty: float | None = None # kg m_fuel: float = 0. # kg m_payload: float = 0. # kg P: float | None = None # N # Geometry S: float | None = None # m2 b: float | None = None # m wing_shape_coefficient: float = 1. wing_to_ground_height: float | None = None # m # Thrust nb_engines: int = 1 thrust_per_engine: float | None = None # N engine_type: Literal["moto-propeller", "turbo-reactor"] = "turbo-reactor" fuel_specific_conso: float | None = None # kg/(N.h) # Aero coefficients C_D_0: float | None = None k: float | None = None C_L_alpha: float | None = None # rad-1 C_L_delta: float | None = None # rad-1 C_m_0: float | None = None C_m_alpha: float | None = None # rad-1 C_m_delta: float | None = None # rad-1 alpha_0: float | None = 0. # rad alpha_stall: float = 15 * np.pi / 180 # rad ground_effect_coefficient: float | None = None C_L_max: float | None = None # Type of atmosphere atmosphere_model = StandardAtmosphere environment_model = EarthEnvironment def __init__(self, plane_data_name: str | None = None, plane_database_folder: str = default_plane_database, plane_parameters_dict: dict | None = None): if plane_data_name is not None: self.load_plane_data(plane_data_name, plane_database_folder) elif plane_parameters_dict is not None: self.set_plane_parameters(plane_parameters_dict) @property def m(self) -> float: """ Total mass of the plane. """ return self.m_empty + self.m_fuel + self.m_payload @property def extension(self) -> float: """ Extension coefficient of the wings. """ return pow(self.b, 2) / self.S @property def f_max(self) -> float: """ Max gliding ratio. """ f_max = 1 / (2 * np.sqrt(self.k * self.C_D_0)) return f_max @property def C_L_f_max(self): """ Lift coefficient at max gliding ratio. """ C_L_star = np.sqrt(self.C_D_0 / self.k) return C_L_star @property def alpha_f_max(self): """ Angle of incidence at max gliding ratio. """ C_L_star = self.C_L_f_max alpha_f_max = C_L_star / self.C_L_alpha + self.alpha_0 return alpha_f_max @property def fuel_specific_conso_SI(self): """ Fuel specific consumption in International unit system. """ return self.fuel_specific_conso / 3600
[docs] def update_k(self, force: bool = False): """ Update the value of k. Parameters ---------- force : bool, optional Force the computation of k even if it already exists, by default False """ if self.k is None or force: self.k = 1 / (np.pi * self.wing_shape_coefficient * self.extension)
[docs] def update_P(self, force: bool = False): """ Update the value of the weight. Parameters ---------- force : bool, optional Force the computation of the weight even if it already exists, by default False """ if self.P is None or force: self.P = self.m * self.environment_model.g
[docs] def update_ground_effect_coefficient(self, force: bool = False): """ Update the value of the ground effect coefficient. Parameters ---------- force : bool, optional Force the computation of the ground effect coefficient even if it already exists, by default False """ if (self.ground_effect_coefficient is None or force) and self.wing_to_ground_height is not None: temp = np.power(16 * self.wing_to_ground_height / self.b, 2) self.ground_effect_coefficient = temp / (1 + temp)
[docs] def update_C_L_max(self, force: bool = False): """ Update the value of the CL max. Parameters ---------- force : bool, optional Force the computation of the CL max even if it already exists, by default False """ if (self.C_L_max is None or force) and\ self.alpha_stall is not None and self.C_L_alpha is not None: self.C_L_max = self.C_L(self.alpha_stall)
[docs] def update_variables(self, force: bool = False): """ Update the value of the variables k, P, ground effect and CL max. Parameters ---------- force : bool, optional Force the computation of the variables even if they already exist, by default False """ self.update_k(force) self.update_P(force) self.update_ground_effect_coefficient(force) self.update_C_L_max(force)
[docs] def C_L(self, alpha: float) -> float: """ Compute the lift coefficient for the given angle of incidence. Parameters ---------- alpha : float Angle of incidence. Returns ------- float Lift coefficient. """ C_L = self.C_L_alpha * (alpha - self.alpha_0) return C_L
[docs] def C_D(self, alpha: float = None, C_L: float = None) -> float: """ Compute the drag coefficient for a given angle of incidence or lift coefficient. Parameters ---------- alpha : float, optional Angle of incidence, by default None C_L : float, optional Lift coefficient, by default None Returns ------- float Drag coefficient. """ # Update k if necessary self.update_k() # Compute the lift coefficient if not provided if C_L is None: C_L = self.C_L(alpha) # Compute the drag coefficient C_D = self.C_D_0 + self.k * pow(C_L, 2) return C_D
[docs] def f(self, alpha: float) -> float: """ Compute the gliding ratio at a given angle of incidence. Parameters ---------- alpha : float Angle of incidence. Returns ------- float Gliding ratio. """ C_L = self.C_L(alpha) C_D = self.C_D(alpha) f = C_L / C_D return f
[docs] def v(self, alpha: float, z: float = 0.) -> float: """ Compute the velocity at a given angle of incidence and altitude. Parameters ---------- alpha : float Angle of incidence. z : float, optional Altitude, by default 0. Returns ------- float Velocity. """ rho = self.atmosphere_model.compute_density_from_altitude(z) v = np.sqrt(self.P / (.5 * rho * self.S * self.C_L(alpha))) return v
[docs] def n(self, v: float, z: float, alpha: float) -> float: """ Compute the loading factor at a given velocity, altitude and angle of incidence. Parameters ---------- v : float Velocity. z : float Altitude. alpha : float Angle of incidence. Returns ------- float Loading factor. """ lift = self.compute_lift(v, z, alpha) n = lift / self.P return n
[docs] def compute_drag(self, v: float, z: float, alpha: float | None = None, C_L: float | None = None) -> float: """ Compute the drag at a given velocity, altitude and angle of incidence or lift coefficient. Parameters ---------- v : float Velocity. z : float Altitude. alpha : float | None, optional Angle of incidence, by default None C_L : float | None, optional Lift coefficient, by default None Returns ------- float Drag force. """ drag = .5 * self.atmosphere_model.compute_density_from_altitude(z) * \ self.S * pow(v, 2) * self.C_D(alpha, C_L) return drag
[docs] def compute_lift(self, v: float, z: float, alpha: float | None = None, C_L: float | None = None) -> float: """ Compute the lift at a given velocity, altitude and angle of incidence or lift coefficient. Parameters ---------- v : float Velocity. z : float Altitude. alpha : float | None, optional Angle of incidence, by default None C_L : float | None, optional Lift coefficient, by default None Returns ------- float Lift force. """ if C_L is None: C_L = self.C_L(alpha) lift = .5 * self.atmosphere_model.compute_density_from_altitude(z) * \ self.S * pow(v, 2) * C_L return lift
[docs] def compute_thrust(self, z: float) -> float: """ Compute the thrust at a given altitude. Parameters ---------- z : float Altitude. Returns ------- float Thrust force. Raises ------ NotImplementedError The engines types other than turbo-reactor are currently not supported. """ if self.engine_type == "turbo-reactor": sigma = self.atmosphere_model.compute_sigma_from_altitude(z) return self.thrust_per_engine * self.nb_engines * sigma else: raise NotImplementedError( "More engines types will be added in the next versions.")
[docs] def compute_normalized_thrust(self, z: float) -> float: """ Compute the normalized thrust at a given altitude. Parameters ---------- z : float Altitude. Returns ------- float Normalized thrust. """ t = self.compute_thrust(z) * self.f_max / self.P return t
[docs] def compute_stall_speed(self, z: float = 0., alpha_stall: float | None = None, C_L_max: float | None = None) -> float: """ Compute the stall speed at a given altitude. Parameters ---------- z : float, optional Altitude, by default 0. alpha_stall : float | None, optional Stall angle, by default None C_L_max : float | None, optional Max lift coefficient, by default None Returns ------- float Stall speed. """ if C_L_max is None: if alpha_stall is None: alpha_stall = self.alpha_stall C_L_max = self.C_L(alpha_stall) rho = self.atmosphere_model.compute_density_from_altitude(z) stall_speed = np.sqrt((2 * self.P) / (rho * self.S * C_L_max)) return stall_speed
[docs] def compute_gliding_speed(self, alpha: float, z: float) -> float: """ Compute the gliding speed at a given angle of incidence and altitude. Parameters ---------- alpha : float Angle of incidence. z : float Altitude. Returns ------- float Gliding speed. """ rho = self.atmosphere_model.compute_density_from_altitude(z) v = np.sqrt((2 * self.P) / (rho * self.S * self.C_L(alpha))) return v
[docs] def compute_min_descent_gliding_slope(self) -> float: """ Compute the minimum descent slope when gliding. Returns ------- float Minimum descent gliding slope. """ gamma_min = -1 / self.f_max return gamma_min
[docs] def compute_v_at_gliding_v_z_min(self, z: float) -> float: """ Compute the velocity associated to the minimum gliding vertical speed at a given altitude. Parameters ---------- z : float Altitude. Returns ------- float Velocity associated to the minimum gliding vertical speed. """ rho = self.atmosphere_model.compute_density_from_altitude(z) v_at_v_z_min = np.sqrt((2 * self.P) / (rho * self.S)) * \ pow(self.k / (3 * self.C_D_0), 1 / 4) return v_at_v_z_min
[docs] def compute_gliding_v_z_min(self, z: float) -> float: """ Compute the minimum gliding vertical speed at a given altitude. Parameters ---------- z : float Altitude. Returns ------- float Minimum gliding vertical speed. """ rho = self.atmosphere_model.compute_density_from_altitude(z) v_z_min = 4 * np.sqrt((2 * self.P) / (rho * self.S)) * \ pow(pow(self.k, 3) * self.C_D_0 / 27, 1 / 4) return v_z_min
[docs] def compute_max_gliding_time(self, z: float) -> float: """ Compute the maximum gliding time at a given altitude. Parameters ---------- z : float Altitude. Returns ------- float Maximum gliding time. """ v_z_min = self.compute_gliding_v_z_min(z) return z / v_z_min
[docs] def compute_velocity_interval_for_fixed_thrust(self, z: float) -> tuple[float, float]: """ Compute the velocity interval that is available with the thrust of the plane at a given altitude. Parameters ---------- z : float Altitude in meters. Warning ------- This velocity interval does not take into account the stall velocity. The minimum velocity can therefore be inferior to the stall velocity. Returns ------- tuple[float,float] Tuple containing the min and max velocity. """ t = self.compute_normalized_thrust(z) v_ref = self.compute_reference_speed(z) v_max = float(np.sqrt(t + np.sqrt(pow(t, 2) - 1)) * v_ref) v_min = float(np.sqrt(t - np.sqrt(pow(t, 2) - 1)) * v_ref) return v_min, v_max
[docs] def compute_reference_speed(self, z: float) -> float: """ Corresponds to the gliding velocity at f_max. Parameters ---------- z : float Altitude of the plane. Returns ------- float Reference velocity. """ rho = self.atmosphere_model.compute_density_from_altitude(z) v_ref = np.sqrt((2 * self.P) / (rho * self.S)) * \ pow(self.k / self.C_D_0, 1 / 4) return v_ref
[docs] def compute_max_gliding_range(self, z: float): """ Compute the max gliding range at a given altitude. Parameters ---------- z : float Altitude. Returns ------- float Max gliding range. """ R_max = self.f_max * z return R_max
[docs] def compute_thrust_needed(self, alpha: float, z: float): """ Compute the thrust needed for the plane at a given angle of incidence and altitude. This does not take into account the stall effect. Parameters ---------- alpha : float Angle of incidence. z : float Altitude. Returns ------- float Thrust needed. """ # Compute the air density rho = self.atmosphere_model.compute_density_from_altitude(z) # Compute the drag sources friction_drag = .5 * rho * self.S * self.v(alpha, z) * self.C_D_0 induced_drag = (2 * self.k * self.P) / \ (rho * self.S * pow(self.v(alpha, z), 2)) # Compute the thrust needed thrust_needed = friction_drag + induced_drag return thrust_needed
[docs] def compute_min_thrust_needed(self): """ Compute the minimal thrust needed to fly. Returns ------- float Minimal thrust in N. """ min_thrust_needed = 2 * self.P * np.sqrt(self.k * self.C_D_0) return min_thrust_needed
[docs] def compute_speed_for_min_thrust_needed(self, z: float) -> float: """ Compute the speed at the minimum thrust. Parameters ---------- z : float Altitude. Returns ------- float Speed at the minimum thrust. """ return self.compute_reference_speed(z)
[docs] def compute_speed_for_min_power_needed(self, z: float) -> float: """ Compute the speed at the minimum power. Parameters ---------- z : float Altitude. Returns ------- float Speed at the minimum power. """ reference_speed = self.compute_reference_speed(z) v_for_w_min = (1 / pow(3, 1 / 4)) * reference_speed return v_for_w_min
[docs] def compute_max_altitude(self) -> float: """ Compute the max altitude that the plane can reach. Returns ------- float Max altitude. """ # Compute the thrust at max glide ratio thrust_needed_at_f_max = self.P / self.f_max # Compute the min sigma at which the plane can fly sigma = thrust_needed_at_f_max / self.compute_thrust(0) # Compute the altitude from sigma z_max = self.atmosphere_model.compute_altitude_from_sigma(sigma) return z_max
[docs] def compute_speed_for_max_ascension_speed(self, z: float) -> float: """ Compute the velocity that gives the maximum ascension speed. Parameters ---------- z : float Altitude in meters. Returns ------- float Velocity giving the maximum ascension speed. """ t = self.compute_normalized_thrust(z) u_m = np.sqrt((t + np.sqrt(np.power(t, 2) + 3)) / 3) v_ref = self.compute_reference_speed(z) return v_ref * u_m
[docs] def compute_max_ascension_speed(self, z: float) -> float: """ Compute the maximum ascension speed. Parameters ---------- z : float Altitude in meters. Returns ------- float Maximum ascension speed. """ t = self.compute_normalized_thrust(z) u_m = np.sqrt((t + np.sqrt(np.power(t, 2) + 3)) / 3) v_ref = self.compute_reference_speed(z) v_z_max = (1 / (2 * self.f_max)) * (2 * t * u_m - (np.power(u_m, 3) + 1 / u_m)) * v_ref return v_z_max
[docs] def compute_ascension_slope(self, alpha: float, z: float) -> float: """ Compute the ascension slope at a given incidence. Parameters ---------- alpha : float Angle of incidence in rad. z : float Altitude in meters. Returns ------- float Ascension slope in rad. """ T = self.compute_thrust(z) gamma = np.arcsin(T / self.P - 1 / self.f(alpha)) return gamma
[docs] def compute_max_ascension_slope(self, z: float): """ Compute the maximum ascension slope. Parameters ---------- z : float Altitude in meters. Returns ------- float Maximum ascension slope in rad. """ t = self.compute_normalized_thrust(z) gamma_max = np.arcsin((t - 1) / self.f_max) return gamma_max
[docs] def compute_load_factor_from_roll_angle(self, phi: float): """ Compute the load angle for a roll at the given angle. Parameters ---------- phi : float Angle of roll. Returns ------- float Load factor. """ n_z = 1 / np.cos(phi) return n_z
[docs] def compute_max_range_at_fixed_altitude(self, z: float): """ Compute the maximum range at a fixed altitude. Parameters ---------- z : float Altitude in meters. Returns ------- float Maximum range in meters. """ rho = self.atmosphere_model.compute_density_from_altitude(z) optimal_C_L = self.C_L_f_max / np.sqrt(3) plane_range = (2 / (self.fuel_specific_conso_SI * self.environment_model.g))\ * (np.sqrt(optimal_C_L) / self.C_D(C_L=optimal_C_L)) * \ np.sqrt(2 / (rho * self.S)) * \ (np.sqrt(self.P) - np.sqrt((self.m_empty + self.m_payload) * self.environment_model.g)) return plane_range
[docs] def compute_range_at_fixed_speed(self, v: float, alpha: float | None = None, f: float | None = None): """ Compute the maximum range at a fixed speed. Parameters ---------- v : float Speed in m.s-1. alpha : float | None, optional Angle of incidence, by default None f : float | None, optional Glide ratio, by default None Returns ------- float Maximum range in meters. """ if f is None: f = self.f(alpha) plane_range = (v / (self.fuel_specific_conso_SI * self.environment_model.g)) * \ f * np.log(self.P / ((self.m_empty + self.m_payload) * self.environment_model.g)) return plane_range
[docs] def compute_endurance(self, alpha: float | None = None, f: float | None = None): """ Compute the amount of time that the plane can stay at the same altitude. Parameters ---------- alpha : float | None, optional Angle of incidence in rad, if not specified the glide ratio will be used instead, by default None f : float | None, optional Glide ratio, if not specified the angle of incidence will be used instead, by default None Returns ------- float Amount of time the plane can stay at the same altitude in seconds. """ if f is None: f = self.f(alpha) endurance = (1 / (self.fuel_specific_conso_SI * self.environment_model.g)) * f * \ np.log(self.P / ((self.m_empty + self.m_payload) * self.environment_model.g)) return endurance
[docs] def compute_take_off_distance_no_friction(self, z: float) -> float: """ Compute the take off distance of the plane without taking the ground friction in account. Parameters ---------- z : float Altitude in meters. Returns ------- float Take off distance in meters. """ T = self.compute_thrust(z) rho = self.atmosphere_model.compute_density_from_altitude(z) d_take_off = (self.P / T) * (1.44 * self.P / self.S) / \ (rho * self.environment_model.g * self.C_L_max) return d_take_off
[docs] def compute_ground_effect(self, alpha: float | None = None, C_L: float | None = None) -> float: """ Compute the additional drag due to the ground effect. Parameters ---------- alpha : float | None, optional Angle of incidence in rad, by default None C_L : float | None, optional Lift coefficient, by default None Returns ------- float Ground effect drag force in N. """ if C_L is None: C_L = self.C_L(alpha) ground_effect = self.ground_effect_coefficient * \ np.power(C_L, 2) / (self.wing_shape_coefficient * np.pi * self.extension) return ground_effect
[docs] def compute_drag_with_ground_effect(self, v: float, z: float, alpha: float | None = None, C_L: float | None = None) -> float: """ Compute the total drag, taking the ground effect into account. Parameters ---------- v : float Velocity in m.s-1 z : float Altitude in m alpha : float | None, optional Angle of incidence in rad, by default None C_L : float | None, optional Lift coefficient, by default None Returns ------- float Drag with ground effect in N. """ ground_effect = self.compute_ground_effect(alpha=alpha, C_L=C_L) rho = self.atmosphere_model.compute_density_from_altitude(z) drag_with_ground_effect = .5 * rho * self.S * \ np.power(v, 2) * (self.C_D_0 + ground_effect) return drag_with_ground_effect
[docs] def compute_take_off_speed(self, z: float): """ Compute the plane take off speed. Parameters ---------- z : float Altitude in meters. Returns ------- float Take off speed. """ rho = self.atmosphere_model.compute_density_from_altitude(z) take_off_speed = 1.2 * \ np.sqrt(2 * self.P / (rho * self.S * self.C_L_max)) return take_off_speed
[docs] def compute_landing_speed(self, z: float): """ Compute the plane landing speed. Parameters ---------- z : float Altitude in meters. Returns ------- float Landing speed. """ rho = self.atmosphere_model.compute_density_from_altitude(z) landing_speed = 1.3 * \ np.sqrt(2 * self.P / (rho * self.S * self.C_L_max)) return landing_speed
[docs] def compute_take_off_distance_with_friction(self, z: float, mu: float, C_L_max: float | None = None): """ Compute the take off distance, taking into account the friction. Parameters ---------- z : float Altitude in meters mu : float Friction coefficient of the landing way. C_L_max : float | None, optional Max lift coefficient, by default None Returns ------- float Take off distance. """ if C_L_max is None: C_L_max = self.C_L_max take_off_speed = self.compute_take_off_speed(z) T = self.compute_thrust(z) D = self.compute_drag_with_ground_effect( take_off_speed * 0.7, z, C_L=C_L_max) L = self.compute_lift(take_off_speed * 0.7, z, C_L=C_L_max) rho = self.atmosphere_model.compute_density_from_altitude(z) d_take_off = (1.44 * (self.P / self.S)) / (rho * self.environment_model.g * C_L_max) * (self.P / (T - (D + mu * (self.P - L)))) return d_take_off
[docs] def compute_landing_distance(self, z: float, mu: float, reverse_thrust: float = 0., C_L: float = 0., C_L_max: float | None = None): """ Compute the landing distance. Parameters ---------- z : float Altitude in meters. mu : float Friction coefficient of the landing way. reverse_thrust : float, optional Reverse thrust force, by default 0. C_L : float | None, optional Lift coefficient used to compute the drag and lift on the landing way, by default 0. C_L_max : float | None, optional Max lift coefficient, by default None Returns ------- float Landing distance. """ if C_L_max is None: C_L_max = self.C_L_max if C_L is None: C_L = C_L_max landing_speed = self.compute_landing_speed(z) D = self.compute_drag(landing_speed * 0.7, z, C_L=C_L) L = self.compute_lift(landing_speed * 0.7, z, C_L=C_L) rho = self.atmosphere_model.compute_density_from_altitude(z) d_landing = (1.69 * (self.P / self.S)) / (rho * self.environment_model.g * C_L_max) * \ (self.P / (reverse_thrust + (D + mu * (self.P - L)))) return d_landing
[docs] def compute_alpha_and_delta_at_flight_point(self, z: float = 0, v: float | None = None): """ Compute the angle of incidence and the angle of control surface of the tailplane at a given flight point. Parameters ---------- z : float, optional Altitude in meters, by default 0 v : float | None, optional Velocity in m.s-1, by default None Returns ------- tuple[float,float] Tuple containing the angle of incidence and angle of control surface for the tail plane. """ rho = self.atmosphere_model.compute_density_from_altitude(z) if v is None: v = self.compute_reference_speed(z) A = np.array([ [self.C_L_alpha, self.C_L_delta], [self.C_m_alpha, self.C_m_delta] ]) B = np.array([ [self.P / (.5 * rho * self.S * np.power(v, 2))], [-self.C_m_0] ]) sol = np.linalg.solve(A, B) alpha = self.alpha_0 + sol[0] delta = sol[1] return alpha, delta
[docs] def plot_polar_graph(self, nb_points: int = 100): """ Plot the polar graph of the plane i.e. lift varying with drag. Parameters ---------- nb_points : int, optional Number of points for the plot, by default 100 """ # Create an array with values for the angle of incidence alpha_array = np.linspace(self.alpha_0, self.alpha_stall, nb_points) # Compute lift coefficient C_L_array = self.C_L(alpha_array) # Compute drag coefficient C_D_array = self.C_D(alpha_array) # Plot the graph plt.plot(C_D_array, C_L_array) plt.xlabel("C_D") plt.ylabel("C_L") plt.title("Polar graph of the plane") plt.show()
[docs] def plot_gliding_TV_graph(self, z: float | list | tuple = 0., nb_points: int = 100): """ Plot the thrust / speed graph of the plane at a given altitude. Parameters ---------- z : float | list | tuple, optional Altitude, by default 0. nb_points : int, optional Number of points for the plot, by default 100 """ # Get the list of colors to use for the plots colors_list = list(mcolors.TABLEAU_COLORS.values()) # Convert z to a list if it is a single value if not isinstance(z, (list, tuple)): z = [z] # Iterate over the values of z compute thrust and speed and plot the curve for i in range(len(z)): alpha_array = np.linspace( self.alpha_0, self.alpha_stall, nb_points) T_array = self.P / self.f(alpha_array) V_array = self.v(alpha_array, z[i]) plt.plot(V_array, T_array, label=f"z={z[i]}", color=colors_list[i]) if self.engine_type == "turbo-reactor" and self.thrust_per_engine is not None: thrust = self.compute_thrust(z[i]) plt.plot([0, np.max(V_array[V_array != np.inf])], [ thrust, thrust], "--", label=f"Thrust at z={z[i]}", color=colors_list[i]) # Add labels plt.legend() plt.xlabel("v") plt.ylabel("T") plt.title("TV graph of the plane") # Show the graph plt.show()
[docs] def plot_gliding_WV_graph(self, z: float | list | tuple = 0., nb_points: int = 100): """ Plot the power / speed graph of the plane at a given altitude. Parameters ---------- z : float | list | tuple, optional Altitude, by default 0. nb_points : int, optional Number of points for the plot, by default 100 """ # Convert z to a list if it is a single value if not isinstance(z, (list, tuple)): z = [z] # Iterate over the values of z to compute speed and power and plot the curve for i in range(len(z)): alpha_array = np.linspace( self.alpha_0, self.alpha_stall, nb_points) T_array = self.P / self.f(alpha_array) V_array = self.v(alpha_array, z[i]) W_array = T_array * V_array plt.plot(V_array, W_array, label=f"z={z[i]}") # Add labels plt.xlabel("v") plt.ylabel("W") plt.title("WV graph of the plane") # Show the graph plt.show()
[docs] def load_plane_data(self, plane_data_name: str, plane_data_folder: str = default_plane_database): """ Load the data from a plane stored in the given database folder. Parameters ---------- plane_data_name : str Name of the plane. plane_data_folder : str, optional Path to the database folder, by default default_plane_database """ # Load the json file file_path = os.path.join(plane_data_folder, plane_data_name + ".json") with open(file_path, "r") as file: plane_data_dict = json.load(file) # Set the parameters self.set_plane_parameters(plane_data_dict)
[docs] def set_plane_parameters(self, plane_data_dict: dict): """ Set the parameters of the plane. Parameters ---------- plane_data_dict : dict Dictionary containing the plane parameters. """ # List the variables allowed in the class plane_allowed_variables_list = dir(self) # Store the values in the class for key in plane_data_dict: if key in plane_allowed_variables_list: setattr(self, key, plane_data_dict[key]) # Update the variables self.update_variables()