A colleague of mine flies planes as a hobby. Sometimes when you are low on fuel you want to find the optimal speed to fly at which will maximize how far you can travel, given the winds you are experiencing. Airplanes will usually come with a manual containing a table of recommended speeds but they do not include all possible headwind-altitude combinations. If your intuition is incorrect or your rough calculations are inaccurate, a situation like this can get pretty sweaty.
He asked if I could make a function for calculating the optimal indicated speed 'v' at different headwinds and altitudes. The Significant Range of an airplane is defined as fuel flow over ground speed. I find the name 'Significant Range' quite unhelpful but hopefully you can imagine that if you can maximize your ground speed while minimizing your fuel flow, you will be able to travel the furthest distance. Therefore, finding the indicated speed which minimizes your Significant Range will be the speed that takes you the furthest distance.
def specific_range(a, b, m, k1, k2, k3, v, W, H): return (a*v + b*m*((k1/k3)*(v**4)+(k2*W**2)/k3))/(v*(m*v - H))
When trying to minimize a function, we want to get to the bottom of the curve of that function. We know we are at the bottom of this curve when the slope of the function equals 0. Getting the derivative of a function at a given point will give us the slope at that point. Here is the derivative of the Significant Range function. If anyone would like to see how this is found, here's a link to my derivation.
def derivative(a, b, m, k1, k2, k3, v, W, H): return (m*(b*((2*k1*m*v**5)/k3 - (3*H*k1*v**4)/k3 - (2*k2*m*W**2*v)/k3 + (H*k2*W**2)/k3) - a*v**2))/(v**2*(m*v - H)**2)
If we find the indicated speed at which the derivative of the function equals 0, that will be the speed that minimizes our Significant Range. Unfortunately this is not a simple task because our derivative function is a quintic polynomial (of degree 5). We can find the roots of lower order polynomials using universal functions such as the quadratic formula (the '-b formula') for quadratics, but with quintics there is no one-stop-shop for finding the roots... so we must be more creative.
Gradient Descent is an algorithm commonly used in AI to minimize very complex functions. In our case, it is an intuitive way to get down to the bottom of the hill of our function. Basically we start off at the top of the hill, check what direction the hill is sloping in, then move a bit in that direction until we reach the bottom. For our Significant Range function, we know that we will not want to move at negative speeds (flying backwards) so we can choose the 'top of the hill' to be 300 knots, which is about the maximum speed of a propeller aircraft. With this choice, we know that going to the bottom of the hill from this point will give us a positive value, as the function slopes towards 0:
If we were to choose a negative value as the 'top of the hill' we would still make it to the bottom of the hill but would end up in the wrong valley:
Here's the Gradient Descent algorithm which returns the best indicated speed starting in the correct position of 300 knots:
def gradient_descent(a, b, m, k1, k2, k3, W, H, num_iters=100, learning_rate=10000): v = 300 for _ in range(num_iters): v = v - learning_rate*derivative(a, b, m, k1, k2, k3, v, W, H) return v
And here's the output using some default values along with a display of how the value was reached:
gradient_descent(16, 39, 1.58, 0.037, 1.5, 469350, 3000, 20)
If you would like to mess around with it yourself and see the actual code along with some explanations, you can check out my Colab Notebook. And as always, if you think I'm an idiot and have made a horrible mistake, please let me know!