protected Instrument getInstrumentFromXml(String instrumentXML) throws Exception {
    BindFactory geometryBindFactory = GeometryBindFactory.getInstance();
    File inputFile = getInputFile(inputInstrumentXML, geometryBindFactory);
    Instrument instrument = (Instrument) geometryBindFactory.unmarshalXml(inputFile, true);
    instrument.updateComponents();

    return instrument;
  }
  // @Test This is legacy and is not being maintained
  public final void testInstrumentOptimization() {
    try {
      Instrument optimizedInstrument = doInstrumentOptimization();

      // Test bore length
      List<BorePoint> borePoints = optimizedInstrument.getBorePoint();
      SortedPositionList<BorePoint> sortedPoints = new SortedPositionList<BorePoint>(borePoints);
      BorePoint lastPoint = sortedPoints.getLast();

      // Test hole positions
      List<Hole> holes = optimizedInstrument.getHole();
      SortedPositionList<Hole> sortedHoles = new SortedPositionList<Hole>(holes);

      System.out.println("Hole position and diameter:");
      for (int i = 0; i < sortedHoles.size(); ++i) {
        System.out.print(sortedHoles.get(i).getBorePosition());
        System.out.print("  ");
        System.out.println(sortedHoles.get(i).getDiameter());
      }

      // System.out.print("last point = " + lastPoint.getBorePosition());
      assertEquals("Bore length incorrect", 338., lastPoint.getBorePosition(), 0.2);

      assertEquals("Hole 1 position incorrect", 102., sortedHoles.get(0).getBorePosition(), 1.5);
      assertEquals("Hole 2 position incorrect", 103., sortedHoles.get(1).getBorePosition(), 1.5);
      assertEquals("Hole 3 position incorrect", 128., sortedHoles.get(2).getBorePosition(), 1.5);
      assertEquals("Hole 4 position incorrect", 138., sortedHoles.get(3).getBorePosition(), 2.5);
      assertEquals("Hole 5 position incorrect", 158., sortedHoles.get(4).getBorePosition(), 2.5);
      assertEquals("Hole 6 position incorrect", 184.7, sortedHoles.get(5).getBorePosition(), 2.5);
      assertEquals("Hole 7 position incorrect", 205.1, sortedHoles.get(6).getBorePosition(), 2.5);
      assertEquals("Hole 8 position incorrect", 225.7, sortedHoles.get(7).getBorePosition(), 2.5);
      assertEquals("Hole 9 position incorrect", 247., sortedHoles.get(8).getBorePosition(), 2.5);
      assertEquals("Hole 10 position incorrect", 277., sortedHoles.get(9).getBorePosition(), 1.5);

      assertEquals("Hole 1 diameter incorrect", 4.0, sortedHoles.get(0).getDiameter(), 0.2);
      assertEquals("Hole 2 diameter incorrect", 4.0, sortedHoles.get(1).getDiameter(), 0.2);
      assertEquals("Hole 3 diameter incorrect", 5.2, sortedHoles.get(2).getDiameter(), 0.4);
      assertEquals("Hole 4 diameter incorrect", 5.7, sortedHoles.get(3).getDiameter(), 0.4);
      assertEquals("Hole 5 diameter incorrect", 6.4, sortedHoles.get(4).getDiameter(), 0.4);
      assertEquals("Hole 6 diameter incorrect", 7.1, sortedHoles.get(5).getDiameter(), 0.4);
      assertEquals("Hole 7 diameter incorrect", 6.4, sortedHoles.get(6).getDiameter(), 0.4);
      assertEquals("Hole 8 diameter incorrect", 6.4, sortedHoles.get(7).getDiameter(), 0.4);
      assertEquals("Hole 9 diameter incorrect", 6.4, sortedHoles.get(8).getDiameter(), 0.5);
      assertEquals("Hole 10 diameter incorrect", 6.8, sortedHoles.get(9).getDiameter(), 0.5);

    } catch (Exception e) {
      fail(e.getMessage());
    }
  }
  public void updateGeometry(double[] state_vector) {
    PositionInterface[] sortedHoles = Instrument.sortList(instrument.getHole());

    for (int i = 0; i < sortedHoles.length; ++i) {
      Hole hole = (Hole) sortedHoles[i];
      hole.setRatio(state_vector[i]);
    }

    instrument.updateComponents();
  }
  /** StateVector has an element for each hole size ratio (hole/bore) */
  public double[] getStateVector() {
    PositionInterface[] sortedHoles = Instrument.sortList(instrument.getHole());

    int len = sortedHoles.length;
    double[] state_vector = new double[len];

    for (int i = 0; i < len; ++i) {
      Hole hole = (Hole) sortedHoles[i];
      state_vector[i] = hole.getRatio();
    }

    return state_vector;
  }
  /**
   * 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;
  }