/**
   * Complete workflow for optimizing an XML-defined instrument with a specified ObjectiveFunction.
   *
   * @return An Instrument object after optimization, with all dimensions in the original units.
   * @throws Exception
   */
  public Instrument doInstrumentOptimization() throws Exception {
    PhysicalParameters parameters = new PhysicalParameters(25., TemperatureType.C);
    Instrument instrument = getInstrumentFromXml(inputInstrumentXML);
    InstrumentCalculator calculator = new SimpleReedCalculator(instrument, parameters);
    instrument.convertToMetres();

    Tuning tuning = getTuningFromXml(inputTuningXML);

    double lowerBound[] =
        new double[] {
          0.32, 0.000, 0.00, 0.00, 0.015, 0.015, 0.015, 0.02, 0.02, 0.02, 0.02, 0.004, 0.004, 0.005,
          0.004, 0.004, 0.004, 0.005, 0.005, 0.005, 0.005
        };
    double upperBound[] =
        new double[] {
          0.38, 0.001, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.100, 0.007, 0.007, 0.007,
          0.007, 0.007, 0.0075, 0.007, 0.007, 0.007, 0.007
        };

    EvaluatorInterface evaluator = new ReflectionEvaluator(calculator);
    BaseObjectiveFunction objective = new HoleObjectiveFunction(calculator, tuning, evaluator);
    objective.setLowerBounds(lowerBound);
    objective.setUpperBounds(upperBound);

    // At present, the tuning is insufficiently constrained to uniquely
    // determine
    // both hole size and position. Slight changes in number of
    // interpolation points
    // can lead to drastic changes in the optimum found.

    ObjectiveFunctionOptimizer.optimizeObjectiveFunction(
        objective, BaseObjectiveFunction.OptimizerType.BOBYQAOptimizer);

    // Convert back to the input unit-of-measure values
    instrument.convertToLengthType();

    // The optimizer modifies the input Instrument instance
    return instrument;
  }