Source code for RCAIDE.Library.Plots.Performance.plot_load_diagram

# RCAIDE/Library/Plots/Mass_Properties/plot_load_diagram.py
# 
# 
# Created:  Aug 2025, M. Clarke

# ----------------------------------------------------------------------------------------------------------------------
#  IMPORT
# ----------------------------------------------------------------------------------------------------------------------  
from RCAIDE.Framework.Core import Units
from RCAIDE.Library.Plots.Common import set_axes, plot_style
import matplotlib.pyplot as plt
from scipy.spatial import ConvexHull
from shapely.geometry import Polygon
import matplotlib.cm as cm
from scipy.interpolate import griddata
import matplotlib.tri as tri
import numpy as np 

# ----------------------------------------------------------------------------------------------------------------------
#  PLOTS
# ---------------------------------------------------------------------------------------------------------------------- 
[docs] def plot_load_diagram(results, save_figure = False, show_legend = True, save_filename = "Aircraft_Loading_Trim_Dragram", file_type = ".png", static_margin_lower_limit = -0.1, static_margin_upper_limit = 1.0, static_margin_resolution = 12, x_axis_lower_limit = None, x_axis_upper_limit = None, y_axis_lower_limit = None, y_axis_upper_limit = None, show_component_vectors = False, width = 11, height = 7): """ Creates a comprehensive aircraft loading diagram showing mass and center of gravity relationships. Parameters ---------- results : RCAIDE.Framework.Core.Data Results from load and trim diagram analysis containing: - loading_mass : numpy.ndarray Aircraft mass for loading diagram [kg] - loading_LEMAC_location : numpy.ndarray LEMAC location as percentage of reference chord [%] - trim_results.LEMAC_location : numpy.ndarray LEMAC location for trim diagram [%] - trim_results.mass : numpy.ndarray Aircraft mass for trim diagram [kg] - trim_results.static_margin : numpy.ndarray Static margin values [unitless] - MTOW : float Maximum takeoff weight [kg] - MLW : float Maximum landing weight [kg] Returns ------- None Creates and displays a matplotlib figure with the loading diagram Notes ----- This function generates a comprehensive aircraft loading diagram that visualizes the relationship between aircraft mass, loading, and center of gravity position. The diagram includes fuel loading curves, payload loading curves, weight limits, and stability contours to provide a complete view of the aircraft's loading envelope. **Major Assumptions** * Convex hull calculation is valid for the data points **Definitions** 'Loading Diagram' Plot showing aircraft mass versus center of gravity position for different loading conditions. 'Convex Hull' Smallest convex polygon that contains all the data points. 'Static Margin' Distance between center of gravity and neutral point as percentage of reference chord. 'LEMAC' Leading Edge Mean Aerodynamic Chord reference point for center of gravity calculations. See Also -------- matplotlib.pyplot scipy.spatial.ConvexHull shapely.geometry.Polygon """ # get plotting style ps = plot_style() parameters = {'axes.labelsize': ps.axis_font_size, 'xtick.labelsize': ps.axis_font_size, 'ytick.labelsize': ps.axis_font_size, 'axes.titlesize': ps.title_font_size} plt.rcParams.update(parameters) fig = plt.figure(save_filename) fig.set_size_inches(width,height) axis = fig.add_subplot(1,1,1) # ------------------------------------------------------------------------ # Stability Contours # ------------------------------------------------------------------------ CG_LEMAC = results.trim_results.CG_percent_of_LEMAC_location*100 SM = results.trim_results.static_margin*100 SM_levels = np.linspace(static_margin_lower_limit*100, static_margin_upper_limit*100, static_margin_resolution) # extend contour grid vertically so it covers the full hull range mass_grid = results.trim_results.mass mass_min = mass_grid.min() mass_max = mass_grid.max() pad_lo = mass_min - 0.1 * (mass_max - mass_min) pad_hi = mass_max + 0.1 * (mass_max - mass_min) CG_LEMAC = np.vstack([CG_LEMAC[0:1, :], CG_LEMAC, CG_LEMAC[-1:, :]]) SM = np.vstack([SM[0:1, :], SM, SM[-1:, :]]) mass_grid = np.vstack([np.full_like(mass_grid[0:1, :], pad_lo), mass_grid, np.full_like(mass_grid[-1:, :], pad_hi)]) CS = axis.contourf(CG_LEMAC, mass_grid, SM, levels = SM_levels, cmap='coolwarm_r', extend='both', alpha = 0.5) CS2 = axis.contour(CG_LEMAC, mass_grid, SM, levels = SM_levels, colors='black', extend='both') cbar = fig.colorbar(CS, ax=axis) axis.clabel(CS2, fontsize=10) cbar.ax.set_ylabel('Static Margin', rotation = 90) # ------------------------------------------------------------------------ # load diamonds # ------------------------------------------------------------------------ # cumulative load vector diamond points = np.hstack(( 100*np.atleast_2d(results.loading_results.CG_percent_of_LEMAC_location.flatten()).T, np.atleast_2d(results.loading_results.mass.flatten()).T )) hull = ConvexHull(points) hull_points = points[hull.vertices] polygon = Polygon(hull_points) x_hull, y_hull = polygon.exterior.xy # fuel vector diamond points = np.hstack(( 100*np.atleast_2d(results.loading_results.CG_percent_of_LEMAC_location[:,0,0,:].flatten()).T, np.atleast_2d(results.loading_results.mass[:,0,0,:].flatten()).T )) hull = ConvexHull(points) hull_points = points[hull.vertices] polygon = Polygon(hull_points) x_hull_f, y_hull_f = polygon.exterior.xy # passenger vector diamond points = np.hstack(( 100*np.atleast_2d(results.loading_results.CG_percent_of_LEMAC_location[:,:,0,0].flatten()).T, np.atleast_2d(results.loading_results.mass[:,:,0,0].flatten()).T )) hull = ConvexHull(points) hull_points = points[hull.vertices] polygon = Polygon(hull_points) x_hull_p, y_hull_p = polygon.exterior.xy # cargo vector diamond points = np.hstack(( 100*np.atleast_2d(results.loading_results.CG_percent_of_LEMAC_location[:,0,:,0].flatten()).T, np.atleast_2d(results.loading_results.mass[:,0,:,0].flatten()).T )) hull = ConvexHull(points) hull_points = points[hull.vertices] polygon = Polygon(hull_points) x_hull_c, y_hull_c = polygon.exterior.xy # ------------------------------------------------------------------------ # PLot Bounds # ------------------------------------------------------------------------ x_bound = max(x_hull) - min(x_hull) if x_axis_lower_limit == None: x_axis_lower_limit = min(x_hull) - x_bound / 2 if x_axis_upper_limit == None: x_axis_upper_limit = max(x_hull) + x_bound / 2 if y_axis_lower_limit == None: y_axis_lower_limit = min(y_hull) if y_axis_upper_limit == None: y_axis_upper_limit = max(y_hull) # ------------------------------------------------------------------------ # Maximum Takeoff Weight line # ------------------------------------------------------------------------ x_pts_MTOW = np.linspace(x_axis_lower_limit, x_axis_upper_limit) y_pts_MTOW = np.ones_like(x_pts_MTOW) * results.MTOW axis.plot(x_pts_MTOW, y_pts_MTOW, 'g-', label = 'MTOW') # ------------------------------------------------------------------------ # Maximum Landing Weight line # ------------------------------------------------------------------------ x_pts_MLW = np.linspace(x_axis_lower_limit, x_axis_upper_limit) y_pts_MLW = np.ones_like(x_pts_MLW) * results.MLW axis.plot(x_pts_MLW, y_pts_MLW, 'g-x', label = 'MLW') # ------------------------------------------------------------------------ # Loading -Trim Bounds # ------------------------------------------------------------------------ axis.fill(x_hull, y_hull, color='grey', alpha=0.3, edgecolor='black', linewidth=2) axis.plot(x_hull, y_hull, 'k-') if show_component_vectors: axis.fill(x_hull_f, y_hull_f, color = 'goldenrod' , alpha=0.3 , edgecolor='goldenrod', linewidth=1) axis.plot(x_hull_f, y_hull_f, color = 'goldenrod' , linestyle = '-',label = "Fuel Loading") axis.fill(x_hull_p, y_hull_p, color = 'darkred' , alpha=0.3 , edgecolor='darkred', linewidth=1) axis.plot(x_hull_p, y_hull_p, color = 'darkred' , linestyle = '-',label = "Pax. Loading") axis.fill(x_hull_c, y_hull_c, color = 'darksalmon' , alpha=0.3 , edgecolor='darksalmon', linewidth=1) axis.plot(x_hull_c, y_hull_c, color = 'darksalmon' , linestyle = '-',label = "Cargo Loading") # ------------------------------------------------------------------------ # Axis Items # ------------------------------------------------------------------------ axis.set_xlim(x_axis_lower_limit, x_axis_upper_limit) axis.set_ylim(y_axis_lower_limit, y_axis_upper_limit) axis.legend(loc='upper right') axis.set_xlabel(r'$X_{CG}$/LEMAC (%)') axis.set_ylabel('Mass (kg)') plt.grid(False) fig.tight_layout() if save_figure: plt.savefig(save_filename + file_type) return