to-9km-and-beyond

Multi-Disciplinary Analysis (MDA) Tool for Subsonic Aircraft Analysis







Project Overview & Methodology

Main Objective

The main objective of this program is to be used to optimize the main wing of an aircraft to beat the FAI record for C-1k planes climbing to 9,000m with a 1,000kg payload (https://fai.org/record/4918), currently held by Gary M. Freeman. In other words, the goal of this program is to be used to optimize a main wing around the airplane's climb time to 9,000m.

For any aircraft configuration (set by the user), this program will also produce best wing* and its corresponding climb time to 9,000m. This climb time can then be prepared to Freeman's 5 min 54s record. Using this program, one can easily design (at least the wing) of an airplane, which can beat Freeman's record.

Note: The program is currently set up to output the simulation data into the console, and produce two .csv files. One csv file is a bunch of wing spans simulated, and one csv file is a bunch of sweep angles simulated for the best span. Currently, the program is set up to only simulate 25 wing spans, and 25 sweep angles*. In order for it to act as an optimizer, increase the number of simulations to 200+ for span and 100+ for sweep angle, or drastically reduce the range.

* The program is set up in the current manner, in order for a user to be able to easily see how it works, without needing to wait 10 mins for a bunch of simulations. A user should expect to only wait around 1-2mins for the program to run with its current set up.

Coding Methodology Overview

A secondary goal of the project was to have the program be robust. Robust meaning that any component could be easily modified - in a safe manner - by any user familiar with C++. Classes (OOP) were used to accomplish this goal.

Every Airplane has (at least) a mainWing, a Horizontal Tail, a Vertical Tail, Engine(s), Nacelle(s), a Fuselage, a fuel weight, and a payload weight. So, each of the corresponding main components (everything that isn't a single float or integer variable) have their own classes.

"This methodology enables the Airplane class to treat each of these components as a 'black box.' It does not matter, from the Airplane Class's point of view, how the Wing object is implemented, just that we have a type Wing, and we can call on its public member functions."

Why C++

When I first started to brainstorm and outline how I wanted to do this project, I realized it would be a lot more readable if an OOP language was used. This is due to the fact that having classes is very desirable for this program (especially for its readability).

C++ is a low level, OOP, programming language, which enables more efficiency than say Python. Personally, I am also more familiar with C++ than any other programming language, since most of the courses I have taken for my Computer Science Minor, have been taught in C++. The cost of getting this extra efficiency is that almost everything had to be built from the ground up (like kadenMath functions).

This project really benefits from being efficient. Since the goal is to optimize an object (the mainWing), it just made a lot of sense to use a language known for efficiency.

A Quick Word on Reusability

By programming using the aforementioned methodologies, the classes I developed in this project are, in many ways black boxes. Therefore, these classes are very re-usable, which is valuable to me because it saves me the time and effort of developing them for my other aero-related personal projects. Additionally, I developed each class to be very readable (to a person familiar with C++), which makes it easier to add to or change their respective implementations.







Software Architecture

The tool is built on a strictly Object-Oriented (OOP) framework in C++, designed to mirror the physical hierarchy of an aircraft. By abstracting components like the Wing, Fuselage, and TurboFan into discrete classes, the architecture allows for highly modular simulations. The Airplane class acts as the orchestrator, pulling aerodynamic data from the DragCoeff and AtmosphereProperties utilities to solve the force balance equations at 0.05-second intervals. This modularity ensures that any component can be swapped—for instance, changing an airfoil profile or engine type—without refactoring the core simulation logic.

MDA Class HierarchyFile Dependency & Linking Diagram
Detailed Class DiagramUML Diagram (Key Idea)






Core Logic

Drag Functions Overview

The drag functions for the three main objects of an airplane (Fuselage, Wing - note a VT, HT, and the mainWing are all “Wing” objects, and Nacelle) are provided by the DragCoeff class. The DragCoeff class has a “calcTotalDragCoeff()” that is implemented to work with any of those three objects.

This function relies on calcParasiteCoeff(), calcInducedCoeff(), calcCompressibilityCoeff(), and calcFormDragCoeff() in order to calculate the total DragCoeff of the object. We assume no separation drag, but in the Airplane Class, we account for this assumption (and other assumptions) by having an EXTRANEOUS_DRAG_MULTIPLIER that is currently set at 15%.

The form factor of any Fuselage object is assumed to be 1.2 and the form factor of any Nacelle object is assumed to be 1.5. This calcTotalDragCoeff() function is used by the Airplane Class (in a calcDragCoeff() - which gives the total airplane drag coefficient at the passed in parameters), namely for power curve functions, but also others.

Power Curve Functions Overview

Since the airplane we are tasked with analyzing in this report has a jet engine, a power curve is necessary to get the best time to climb. (Currently the project doesn’t work with a propeller). As will be explained later, the power curve for any airplane object is calculated at a time step (say 0.05s).

So in order to understand how much excess power the airplane has (and therefore its max rate of climb (RoC)), we need to generate the power curve and be able to calculate where the max excess power is and how much excess power the airplane currently has. This implementation is done discreetly through calcPowerCurveVelocityData(), calcPowerRequiredData(), and calcPowerAvailableData(). calcPowerAvailableData() depends on the CF_34_3B1, which has the thrust curves digitized.

Power Curve ExamplePower Curve Example

There are also two implementations of some of these functions (namely calcPowerRequiredData()), which differ depending on accounting for flight path angle (gamma) or not. In other words, one implementation assumes a small angle approx (power curve does not depend on gamma), while the other is the accurate power curve. Currently, due not being able to easily solve for gamma (and the time not changing significantly when experimenting with a constant gamma), the power curve functions independent of gamma (small angle approx versions) are used the most.

This program provides (in the Airplane class) a function getPowerCurveCSV(height), that produces the power curve of the initialized Airplane object, at any height the user passes in. Recall that the climb functions rely on calculating and analyzing this curve at very small time steps, so this function is more for the user to understand (and verify) that the power curves, which again are the backbone of the climb functions, make sense. It is also important to note that the implementation of calculating the maxExcessPower() and currentExcessPower() is done by custom math functions in “kadenMath.h”.

Time to Climb Function Overview

The short version of the climb function is, it's an implementation based on time steps. The power curve is calculated and solved at every time step. If the velocity for max excess power is outside of a certain error, a steady level acceleration function is called to accelerate to that velocity (and then climb continues at the new velocity).

If the velocity is inside the error, then we calculate the current excess power (not necessarily the max because of the error), and use that for our RoC. (Essentially height += (currentExcessPower / totalWeight) * TIME_STEP). See the implementation of calcBestTimeTo9km() which relies on calcTakeoffPropertites() and calcBestClimbTime() (which relies on calcSteadyLevelAccelerationTime()) in the Airplane Class for more details.

Finally, this program (in the Airplane class) also provides a getFlightEnvelopeCSV(). This flight envelope is by no means perfect, and does not have a bunch of constraints like structural feasibility, but it can still be useful to show that the program is working as intended (by the graph having resembling a typical flight envelope) and as a quick (first take) graph that can be used to compare different airplane configurations quickly.

Rough Flight EnvelopeRough Flight Envelope

Note: It would be fairly quick to implement a block of logic in the calcTimeTo9km() to output CL or CD of the airplane as a function of height, but I simply have spent 120 hours on this project and I simply don’t have the time to implement that at the moment.

"Optimizers"

Read “main() function - Optimizing the mainWing,” in particular the “Overview / Approach:” subsection on Github. A span and sweep angle optimizer are implemented. The optimizer is technically not a pure optimizer because it does not automatically look for the best plane (it runs a user inputted amount of simulations across a range given by the user and produces a CSV with that data).

Span Example 1
Span Example 1

Plot it, then visually inspect, then change the range you pass into the optimizer. I believe my implementation is more useful (and the user can use it as an optimizer, as we do later in this report, but very technically it isn’t). Additionally, to make it act more like an optimizer, the data is sorted in ascending climb time orders, and also outputted to the command window (also the sorted data is the data produced in the CSV).







Results & Optimization Data

Airplane Configuration Span Optimizer was run on:

Optimization Configuration CodeOptimization Setup Parameters (C++)
  • Airfoil: NACA 2412
  • Given Airplane Weight = 23000 * 2.20462 = 50706.26 lbm
  • Starting Fuel Weight = 1000 * 2.20462 = 2204.62 lbm
  • Payload Weight = 1000 * 2.20462 = 2204.62 lbm
  • Adjusted Base Weight = 42957.02 lbm
  • Fuselage Wetted Area = 811.55 ft²
  • Nacelle Wetted Area = 159.87 ft²
  • Main Root Chord = 12.0 ft
  • Main Tip Chord = 4.8 ft
Initial Span Optimizer
Initial Span Optimizer (20ft - 450ft)

Initial Span Range

For this proof of concept, we set a very large range (20ft - 450ft) with 50 simulations run evenly spaced between this distance. This range was decided as anything less than 20ft is not feasible for takeoff, and then we chose an absurdly large span to capture the extreme case (even if they aren’t structurally feasible). As seen in the figure on the left, when plotted, we can see that a smaller span is not efficient; increasing the span to a higher length also eventually leads to no increase in the time to climb. This observation led us to our first revision of our span range.

First Pass Optimization

For our first (more) realistic span optimization we chose a range of 20-150ft with 300 simulations evenly spaced between the distances. In the figure on the right can be seen a more detailed trend seen as compared to our previous optimization run. The time to climb initially decreases sharply as span increases, but eventually reaches a minimum and begins to increase again. This leads us to our next tested range of 65-75ft where with one last pass we find an optimal span.

First Pass Span Optimization
First Pass Span Optimization
Detailed Span Optimization
Detailed Span Optimization

Final Span Refinement

In our final span optimization we ran a range of 65-75ft with 200 simulations evenly spaced between the distances. In the figure on the left, we see less of a variation between the times to climb. When the data is all sorted and analyzed, we found the optimal span to be at around 69.35ft with a time of 4.06909 mins.

Sweep Angle Optimization

With our Span optimized we can now optimize our sweep angle. We take a similar approach and start with a wide range of 0-60 degrees, with 250 simulations evenly spaced between the angle values. Here we can see the lowest and highest values of the sweep angle are not optimal, so we will focus on the center 15-30 degrees for our second optimization runs.

We then narrowed the span angle range to 15-30 degrees and with 250 simulations evenly spaced between the values. We found that with a span of 69.35ft and optimal sweep of 19.957 degrees, this would give us a final time to climb of 3.958 mins.

Sweep Angle Optimization - Initial Pass
Initial Sweep Range (0-60°)

Optimized Results Summary

Climb Performance:3.958 min
Target Altitude:9.0 km
Optimized Span:69.35 ft
Sweep Angle:19.957°
Total Weight:24,141.8 kg
Wing (Comp):2,517.25 lbm

● Wing Weight (Steel) = 2,961.47528 lbm

● Total Airplane Weight (Composite) = 53,223.51398 lbm

Note: Any airplanes below the 25,000 kg minimum takeoff weight were automatically adjusted. Our simulation assumes composite wings (SMUDGE_FACTOR = 0.85).