In this final example, we show a real complex engineering application: the optimization of 24 parameters for a six-gear geartrain. One of the main things to learn from this example is the use of a dependent analysis which is needed by nearly all the constraints and the objective function. Many times, one needs to run a simulation on a candidate solution – from which other functions extract meaningful data. This is the case for the gear train problem. The example, is more about the set-up as opposed to the optimization. I tried Gradient Based, Random Hill Climbing and Genetic Algorithm. It is unclear what is best to solve this problem. Experience tells me that since there are discrete and continuous variables and the space should be fairly smooth, if not uni-modal - that some Branch-n-bound combined with GRG or SQP would be best.
In examining the main source code for this problem, we come to the Dependent Analysis after a long setup of initial inputs and constants, . Take a look at the file/class ForceVelocityPositionAnalysis.cs and notice that it inherits from IDependentAnalysis. By adding this to the optimization method (see below), we are ensuring that it is called for any new decision variables found in the process.
var FVPAnalysis = new ForceVelocityPositionAnalysis(numGears, outputTorque, inputSpeed, inputPosition);
Next, all of the constraints and the objective are declared. Many of these use information found in the Dependent Analysis. There may be a variety of ways to do this, but the approach I take here is to pass the Dependent Analysis object, FVPAnalysis, to the pertinent functions so that they can simply reference values within the DependentAnalysis. For example, the objective function is to minimize mass. It’s constructor receives the FVPAnalysis, and the gearDensity:
public massObjective(ForceVelocityPositionAnalysis fvpsAnalysis, double gearDensity)
this.FVPAnalysis = fvpsAnalysis;
this.numberGears = fvpsAnalysis.numGears;
this.gearDensity = gearDensity;
We see that the Dependent Analysis is stored with an identical local name (of course, the naming is arbitrary – I just uncreatively used the same name).
And then in the calculate(x) function of the massObjective class, I can grab values for the diameter of each gear, as they were determined as part of the DependentAnalysis:
var faceWidth = x[i * 4 + 2];
totMass += Math.PI * Math.Pow((FVPAnalysis.diameters[i] / 2), 2) * faceWidth * gearDensity;
Tn OOOT, the DependentAnalysisis always guaranteed to be called before the objective function or constraints that utilize it, UNLESS you directly call the calculate(x) function. It is best to call the abstractOptMethod.calculate(optFunction, x) function instead as this checks to see if it is a repeat point to something it’s already calculated or whether the DependentAnalysisneeds to be re-run.