1 package net.sf.openrocket.gui.dialogs.optimization;
3 import java.awt.Component;
4 import java.awt.Window;
5 import java.awt.event.ActionEvent;
6 import java.awt.event.ActionListener;
7 import java.awt.event.MouseAdapter;
8 import java.awt.event.MouseEvent;
9 import java.io.BufferedWriter;
11 import java.io.FileWriter;
12 import java.io.IOException;
13 import java.io.Writer;
14 import java.text.Collator;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.Comparator;
18 import java.util.HashMap;
19 import java.util.LinkedHashMap;
20 import java.util.LinkedList;
21 import java.util.List;
24 import javax.swing.BorderFactory;
25 import javax.swing.DefaultComboBoxModel;
26 import javax.swing.JButton;
27 import javax.swing.JCheckBox;
28 import javax.swing.JComboBox;
29 import javax.swing.JComponent;
30 import javax.swing.JDialog;
31 import javax.swing.JFileChooser;
32 import javax.swing.JLabel;
33 import javax.swing.JOptionPane;
34 import javax.swing.JPanel;
35 import javax.swing.JScrollPane;
36 import javax.swing.JSpinner;
37 import javax.swing.JTable;
38 import javax.swing.JToggleButton;
39 import javax.swing.ListSelectionModel;
40 import javax.swing.border.TitledBorder;
41 import javax.swing.event.ChangeEvent;
42 import javax.swing.event.ChangeListener;
43 import javax.swing.event.ListSelectionEvent;
44 import javax.swing.event.ListSelectionListener;
45 import javax.swing.event.TreeSelectionEvent;
46 import javax.swing.event.TreeSelectionListener;
47 import javax.swing.table.AbstractTableModel;
48 import javax.swing.table.DefaultTableCellRenderer;
49 import javax.swing.table.TableColumnModel;
50 import javax.swing.tree.DefaultMutableTreeNode;
51 import javax.swing.tree.TreePath;
53 import net.miginfocom.swing.MigLayout;
54 import net.sf.openrocket.document.OpenRocketDocument;
55 import net.sf.openrocket.document.Simulation;
56 import net.sf.openrocket.gui.SpinnerEditor;
57 import net.sf.openrocket.gui.adaptors.DoubleModel;
58 import net.sf.openrocket.gui.components.CsvOptionPanel;
59 import net.sf.openrocket.gui.components.DescriptionArea;
60 import net.sf.openrocket.gui.components.DoubleCellEditor;
61 import net.sf.openrocket.gui.components.StyledLabel;
62 import net.sf.openrocket.gui.components.StyledLabel.Style;
63 import net.sf.openrocket.gui.components.UnitCellEditor;
64 import net.sf.openrocket.gui.components.UnitSelector;
65 import net.sf.openrocket.gui.scalefigure.RocketFigure;
66 import net.sf.openrocket.gui.scalefigure.ScaleScrollPane;
67 import net.sf.openrocket.l10n.Translator;
68 import net.sf.openrocket.logging.LogHelper;
69 import net.sf.openrocket.optimization.general.OptimizationException;
70 import net.sf.openrocket.optimization.general.Point;
71 import net.sf.openrocket.optimization.rocketoptimization.OptimizableParameter;
72 import net.sf.openrocket.optimization.rocketoptimization.OptimizationGoal;
73 import net.sf.openrocket.optimization.rocketoptimization.SimulationDomain;
74 import net.sf.openrocket.optimization.rocketoptimization.SimulationModifier;
75 import net.sf.openrocket.optimization.rocketoptimization.domains.IdentitySimulationDomain;
76 import net.sf.openrocket.optimization.rocketoptimization.domains.StabilityDomain;
77 import net.sf.openrocket.optimization.rocketoptimization.goals.MaximizationGoal;
78 import net.sf.openrocket.optimization.rocketoptimization.goals.MinimizationGoal;
79 import net.sf.openrocket.optimization.rocketoptimization.goals.ValueSeekGoal;
80 import net.sf.openrocket.optimization.services.OptimizationServiceHelper;
81 import net.sf.openrocket.rocketcomponent.Rocket;
82 import net.sf.openrocket.rocketcomponent.RocketComponent;
83 import net.sf.openrocket.startup.Application;
84 import net.sf.openrocket.unit.CaliberUnit;
85 import net.sf.openrocket.unit.Unit;
86 import net.sf.openrocket.unit.UnitGroup;
87 import net.sf.openrocket.unit.Value;
88 import net.sf.openrocket.util.BugException;
89 import net.sf.openrocket.util.Chars;
90 import net.sf.openrocket.util.FileHelper;
91 import net.sf.openrocket.util.GUIUtil;
92 import net.sf.openrocket.util.Named;
93 import net.sf.openrocket.util.Prefs;
94 import net.sf.openrocket.util.TextUtil;
96 import com.itextpdf.text.Font;
99 * General rocket optimization dialog.
101 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
103 public class GeneralOptimizationDialog extends JDialog {
104 private static final LogHelper log = Application.getLogger();
105 private static final Translator trans = Application.getTranslator();
107 private static final Collator collator = Collator.getInstance();
110 private static final String GOAL_MAXIMIZE = trans.get("goal.maximize");
111 private static final String GOAL_MINIMIZE = trans.get("goal.minimize");
112 private static final String GOAL_SEEK = trans.get("goal.seek");
114 private static final String START_TEXT = trans.get("btn.start");
115 private static final String STOP_TEXT = trans.get("btn.stop");
119 private final List<OptimizableParameter> optimizationParameters = new ArrayList<OptimizableParameter>();
120 private final Map<Object, List<SimulationModifier>> simulationModifiers =
121 new HashMap<Object, List<SimulationModifier>>();
124 private final OpenRocketDocument baseDocument;
125 private OpenRocketDocument documentCopy;
128 private final JButton addButton;
129 private final JButton removeButton;
130 private final JButton removeAllButton;
132 private final ParameterSelectionTableModel selectedModifierTableModel;
133 private final JTable selectedModifierTable;
134 private final DescriptionArea selectedModifierDescription;
135 private final SimulationModifierTree availableModifierTree;
137 private final JComboBox simulationSelectionCombo;
138 private final JComboBox optimizationParameterCombo;
140 private final JComboBox optimizationGoalCombo;
141 private final JSpinner optimizationGoalSpinner;
142 private final UnitSelector optimizationGoalUnitSelector;
143 private final DoubleModel optimizationSeekValue;
145 private DoubleModel minimumStability;
146 private DoubleModel maximumStability;
147 private final JCheckBox minimumStabilitySelected;
148 private final JSpinner minimumStabilitySpinner;
149 private final UnitSelector minimumStabilityUnitSelector;
150 private final JCheckBox maximumStabilitySelected;
151 private final JSpinner maximumStabilitySpinner;
152 private final UnitSelector maximumStabilityUnitSelector;
154 private final JLabel bestValueLabel;
155 private final JLabel stepCountLabel;
156 private final JLabel evaluationCountLabel;
157 private final JLabel stepSizeLabel;
159 private final RocketFigure figure;
160 private final JToggleButton startButton;
161 private final JButton plotButton;
162 private final JButton saveButton;
164 private final JButton applyButton;
165 private final JButton resetButton;
166 private final JButton closeButton;
168 private final List<SimulationModifier> selectedModifiers = new ArrayList<SimulationModifier>();
170 /** List of components to disable while optimization is running */
171 private final List<JComponent> disableComponents = new ArrayList<JComponent>();
173 /** Whether optimization is currently running or not */
174 private boolean running = false;
175 /** The optimization worker that is running */
176 private OptimizationWorker worker = null;
179 private double bestValue = Double.NaN;
180 private Unit bestValueUnit = Unit.NOUNIT2;
181 private int stepCount = 0;
182 private int evaluationCount = 0;
183 private double stepSize = 0;
185 private final Map<Point, FunctionEvaluationData> evaluationHistory = new LinkedHashMap<Point, FunctionEvaluationData>();
186 private final List<Point> optimizationPath = new LinkedList<Point>();
189 private boolean updating = false;
195 * @param document the document
196 * @param parent the parent window
198 public GeneralOptimizationDialog(OpenRocketDocument document, Window parent) {
199 super(parent, trans.get("title"));
201 this.baseDocument = document;
202 this.documentCopy = document.copy();
204 loadOptimizationParameters();
205 loadSimulationModifiers();
212 JPanel panel = new JPanel(new MigLayout("fill"));
215 ChangeListener clearHistoryChangeListener = new ChangeListener() {
217 public void stateChanged(ChangeEvent e) {
221 ActionListener clearHistoryActionListener = new ActionListener() {
223 public void actionPerformed(ActionEvent e) {
230 //// Selected modifiers table
232 selectedModifierTableModel = new ParameterSelectionTableModel();
233 selectedModifierTable = new JTable(selectedModifierTableModel);
234 selectedModifierTable.setDefaultRenderer(Double.class, new DoubleCellRenderer());
235 selectedModifierTable.setRowSelectionAllowed(true);
236 selectedModifierTable.setColumnSelectionAllowed(false);
237 selectedModifierTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
239 // Make sure spinner editor fits into the cell height
240 selectedModifierTable.setRowHeight(new JSpinner().getPreferredSize().height - 4);
242 selectedModifierTable.setDefaultEditor(Double.class, new DoubleCellEditor());
243 selectedModifierTable.setDefaultEditor(Unit.class, new UnitCellEditor() {
245 protected UnitGroup getUnitGroup(Unit value, int row, int column) {
246 return selectedModifiers.get(row).getUnitGroup();
250 disableComponents.add(selectedModifierTable);
252 selectedModifierTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
254 public void valueChanged(ListSelectionEvent e) {
260 TableColumnModel columnModel = selectedModifierTable.getColumnModel();
261 columnModel.getColumn(0).setPreferredWidth(150);
262 columnModel.getColumn(1).setPreferredWidth(40);
263 columnModel.getColumn(2).setPreferredWidth(40);
264 columnModel.getColumn(3).setPreferredWidth(40);
266 scroll = new JScrollPane(selectedModifierTable);
268 label = new StyledLabel(trans.get("lbl.paramsToOptimize"), Style.BOLD);
269 disableComponents.add(label);
270 panel.add(label, "split 3, flowy");
271 panel.add(scroll, "wmin 300lp, height 200lp, grow");
272 selectedModifierDescription = new DescriptionArea(2, -3);
273 disableComponents.add(selectedModifierDescription);
274 panel.add(selectedModifierDescription, "growx");
278 //// Add/remove buttons
279 sub = new JPanel(new MigLayout("fill"));
281 addButton = new JButton(Chars.LEFT_ARROW + " " + trans.get("btn.add") + " ");
282 addButton.setToolTipText(trans.get("btn.add.ttip"));
283 addButton.addActionListener(new ActionListener() {
285 public void actionPerformed(ActionEvent e) {
286 SimulationModifier mod = getSelectedAvailableModifier();
291 log.error("Attempting to add simulation modifier when none is selected");
295 disableComponents.add(addButton);
296 sub.add(addButton, "wrap para, sg button");
298 removeButton = new JButton(" " + trans.get("btn.remove") + " " + Chars.RIGHT_ARROW);
299 removeButton.setToolTipText(trans.get("btn.remove.ttip"));
300 removeButton.addActionListener(new ActionListener() {
302 public void actionPerformed(ActionEvent e) {
303 SimulationModifier mod = getSelectedModifier();
305 log.error("Attempting to remove simulation modifier when none is selected");
312 disableComponents.add(removeButton);
313 sub.add(removeButton, "wrap para*2, sg button");
315 removeAllButton = new JButton(trans.get("btn.removeAll"));
316 removeAllButton.setToolTipText(trans.get("btn.removeAll.ttip"));
317 removeAllButton.addActionListener(new ActionListener() {
319 public void actionPerformed(ActionEvent e) {
320 log.user("Removing all selected modifiers");
321 selectedModifiers.clear();
322 selectedModifierTableModel.fireTableDataChanged();
323 availableModifierTree.repaint();
327 disableComponents.add(removeAllButton);
328 sub.add(removeAllButton, "wrap para, sg button");
334 //// Available modifier tree
335 availableModifierTree = new SimulationModifierTree(documentCopy.getRocket(), simulationModifiers, selectedModifiers);
336 availableModifierTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
338 public void valueChanged(TreeSelectionEvent e) {
343 // Handle double-click
344 availableModifierTree.addMouseListener(new MouseAdapter() {
346 public void mousePressed(MouseEvent e) {
347 if (e.getClickCount() == 2) {
348 SimulationModifier mod = getSelectedAvailableModifier();
353 log.user("Double-clicked non-available option");
359 disableComponents.add(availableModifierTree);
360 scroll = new JScrollPane(availableModifierTree);
361 label = new StyledLabel(trans.get("lbl.availableParams"), Style.BOLD);
362 disableComponents.add(label);
363 panel.add(label, "split 2, flowy");
364 panel.add(scroll, "width 300lp, height 200lp, grow, wrap para*2");
369 //// Optimization options sub-panel
371 sub = new JPanel(new MigLayout("fill"));
372 TitledBorder border = BorderFactory.createTitledBorder(trans.get("lbl.optimizationOpts"));
373 GUIUtil.changeFontStyle(border, Font.BOLD);
374 sub.setBorder(border);
375 disableComponents.add(sub);
378 //// Simulation to optimize
380 label = new JLabel(trans.get("lbl.optimizeSim"));
381 tip = trans.get("lbl.optimizeSim.ttip");
382 label.setToolTipText(tip);
383 disableComponents.add(label);
386 simulationSelectionCombo = new JComboBox();
387 simulationSelectionCombo.setToolTipText(tip);
388 populateSimulations();
389 simulationSelectionCombo.addActionListener(clearHistoryActionListener);
390 disableComponents.add(simulationSelectionCombo);
391 sub.add(simulationSelectionCombo, "growx, wrap unrel");
395 //// Value to optimize
396 label = new JLabel(trans.get("lbl.optimizeValue"));
397 tip = trans.get("lbl.optimizeValue.ttip");
398 label.setToolTipText(tip);
399 disableComponents.add(label);
402 optimizationParameterCombo = new JComboBox();
403 optimizationParameterCombo.setToolTipText(tip);
404 populateParameters();
405 optimizationParameterCombo.addActionListener(clearHistoryActionListener);
406 disableComponents.add(optimizationParameterCombo);
407 sub.add(optimizationParameterCombo, "growx, wrap unrel");
411 //// Optimization goal
412 label = new JLabel(trans.get("lbl.optimizeGoal"));
413 tip = trans.get("lbl.optimizeGoal");
414 label.setToolTipText(tip);
415 disableComponents.add(label);
418 optimizationGoalCombo = new JComboBox(new String[] { GOAL_MAXIMIZE, GOAL_MINIMIZE, GOAL_SEEK });
419 optimizationGoalCombo.setToolTipText(tip);
420 optimizationGoalCombo.setEditable(false);
421 optimizationGoalCombo.addActionListener(clearHistoryActionListener);
422 disableComponents.add(optimizationGoalCombo);
423 sub.add(optimizationGoalCombo, "growx");
426 //// Optimization custom value
427 optimizationSeekValue = new DoubleModel(0, UnitGroup.UNITS_NONE);
428 optimizationSeekValue.addChangeListener(clearHistoryChangeListener);
430 optimizationGoalSpinner = new JSpinner(optimizationSeekValue.getSpinnerModel());
431 tip = trans.get("lbl.optimizeGoalValue.ttip");
432 optimizationGoalSpinner.setToolTipText(tip);
433 optimizationGoalSpinner.setEditor(new SpinnerEditor(optimizationGoalSpinner));
434 disableComponents.add(optimizationGoalSpinner);
435 sub.add(optimizationGoalSpinner, "width 30lp");
437 optimizationGoalUnitSelector = new UnitSelector(optimizationSeekValue);
438 optimizationGoalUnitSelector.setToolTipText(tip);
439 disableComponents.add(optimizationGoalUnitSelector);
440 sub.add(optimizationGoalUnitSelector, "width 20lp, wrap unrel");
443 panel.add(sub, "grow");
447 //// Required stability sub-panel
449 sub = new JPanel(new MigLayout("fill"));
450 border = BorderFactory.createTitledBorder(trans.get("lbl.requireStability"));
451 GUIUtil.changeFontStyle(border, Font.BOLD);
452 sub.setBorder(border);
453 disableComponents.add(sub);
457 double ref = CaliberUnit.calculateCaliber(baseDocument.getRocket());
458 minimumStability = new DoubleModel(ref, UnitGroup.stabilityUnits(ref));
459 maximumStability = new DoubleModel(5 * ref, UnitGroup.stabilityUnits(ref));
460 minimumStability.addChangeListener(clearHistoryChangeListener);
461 maximumStability.addChangeListener(clearHistoryChangeListener);
464 //// Minimum stability
465 tip = trans.get("lbl.requireMinStability.ttip");
466 minimumStabilitySelected = new JCheckBox(trans.get("lbl.requireMinStability"));
467 minimumStabilitySelected.setSelected(true);
468 minimumStabilitySelected.setToolTipText(tip);
469 minimumStabilitySelected.addActionListener(new ActionListener() {
471 public void actionPerformed(ActionEvent e) {
475 disableComponents.add(minimumStabilitySelected);
476 sub.add(minimumStabilitySelected);
478 minimumStabilitySpinner = new JSpinner(minimumStability.getSpinnerModel());
479 minimumStabilitySpinner.setToolTipText(tip);
480 minimumStabilitySpinner.setEditor(new SpinnerEditor(minimumStabilitySpinner));
481 disableComponents.add(minimumStabilitySpinner);
482 sub.add(minimumStabilitySpinner, "growx");
484 minimumStabilityUnitSelector = new UnitSelector(minimumStability);
485 minimumStabilityUnitSelector.setToolTipText(tip);
486 disableComponents.add(minimumStabilityUnitSelector);
487 sub.add(minimumStabilityUnitSelector, "growx, wrap unrel");
490 //// Maximum stability
491 tip = trans.get("lbl.requireMaxStability.ttip");
492 maximumStabilitySelected = new JCheckBox(trans.get("lbl.requireMaxStability"));
493 maximumStabilitySelected.setToolTipText(tip);
494 maximumStabilitySelected.addActionListener(new ActionListener() {
496 public void actionPerformed(ActionEvent e) {
500 disableComponents.add(maximumStabilitySelected);
501 sub.add(maximumStabilitySelected);
503 maximumStabilitySpinner = new JSpinner(maximumStability.getSpinnerModel());
504 maximumStabilitySpinner.setToolTipText(tip);
505 maximumStabilitySpinner.setEditor(new SpinnerEditor(maximumStabilitySpinner));
506 disableComponents.add(maximumStabilitySpinner);
507 sub.add(maximumStabilitySpinner, "growx");
509 maximumStabilityUnitSelector = new UnitSelector(maximumStability);
510 maximumStabilityUnitSelector.setToolTipText(tip);
511 disableComponents.add(maximumStabilityUnitSelector);
512 sub.add(maximumStabilityUnitSelector, "growx, wrap para");
516 // DescriptionArea desc = new DescriptionArea("Stability requirements are verified during each time step of the simulation.",
518 // desc.setViewportBorder(null);
519 // disableComponents.add(desc);
520 // sub.add(desc, "span, growx");
523 panel.add(sub, "span 2, grow, wrap para*2");
529 figure = new RocketFigure(getSelectedSimulation().getConfiguration());
530 figure.setBorderPixels(1, 1);
531 ScaleScrollPane figureScrollPane = new ScaleScrollPane(figure);
532 figureScrollPane.setFitting(true);
533 panel.add(figureScrollPane, "span, split, height 200lp, grow");
536 sub = new JPanel(new MigLayout("fill"));
539 label = new JLabel(trans.get("status.bestValue"));
540 tip = trans.get("status.bestValue.ttip");
541 label.setToolTipText(tip);
542 sub.add(label, "gapright unrel");
544 bestValueLabel = new JLabel();
545 bestValueLabel.setToolTipText(tip);
546 sub.add(bestValueLabel, "wmin 60lp, wrap rel");
549 label = new JLabel(trans.get("status.stepCount"));
550 tip = trans.get("status.stepCount.ttip");
551 label.setToolTipText(tip);
552 sub.add(label, "gapright unrel");
554 stepCountLabel = new JLabel();
555 stepCountLabel.setToolTipText(tip);
556 sub.add(stepCountLabel, "wrap rel");
559 label = new JLabel(trans.get("status.evalCount"));
560 tip = trans.get("status.evalCount");
561 label.setToolTipText(tip);
562 sub.add(label, "gapright unrel");
564 evaluationCountLabel = new JLabel();
565 evaluationCountLabel.setToolTipText(tip);
566 sub.add(evaluationCountLabel, "wrap rel");
569 label = new JLabel(trans.get("status.stepSize"));
570 tip = trans.get("status.stepSize.ttip");
571 label.setToolTipText(tip);
572 sub.add(label, "gapright unrel");
574 stepSizeLabel = new JLabel();
575 stepSizeLabel.setToolTipText(tip);
576 sub.add(stepSizeLabel, "wrap para");
579 //// Start/Stop button
581 startButton = new JToggleButton(START_TEXT);
582 startButton.addActionListener(new ActionListener() {
584 public void actionPerformed(ActionEvent e) {
586 log.debug("Updating, ignoring event");
590 log.user("Stopping optimization");
593 log.user("Starting optimization");
598 sub.add(startButton, "span, growx, wrap para*2");
601 plotButton = new JButton(trans.get("btn.plotPath"));
602 plotButton.setToolTipText(trans.get("btn.plotPath.ttip"));
603 plotButton.addActionListener(new ActionListener() {
605 public void actionPerformed(ActionEvent e) {
606 log.user("Plotting optimization path, dimensionality=" + selectedModifiers.size());
607 OptimizationPlotDialog dialog = new OptimizationPlotDialog(optimizationPath, evaluationHistory,
608 selectedModifiers, getSelectedParameter(),
609 UnitGroup.stabilityUnits(getSelectedSimulation().getRocket()),
610 GeneralOptimizationDialog.this);
611 dialog.setVisible(true);
614 disableComponents.add(plotButton);
615 sub.add(plotButton, "span, growx, wrap");
618 saveButton = new JButton(trans.get("btn.save"));
619 saveButton.setToolTipText(trans.get("btn.save.ttip"));
620 saveButton.addActionListener(new ActionListener() {
622 public void actionPerformed(ActionEvent e) {
623 log.user("User selected save path");
627 disableComponents.add(saveButton);
628 sub.add(saveButton, "span, growx");
632 panel.add(sub, "wrap para*2");
639 applyButton = new JButton(trans.get("btn.apply"));
640 applyButton.setToolTipText(trans.get("btn.apply.ttip"));
641 applyButton.addActionListener(new ActionListener() {
643 public void actionPerformed(ActionEvent e) {
644 log.user("Applying optimization changes");
648 disableComponents.add(applyButton);
649 panel.add(applyButton, "span, split, gapright para, right");
651 resetButton = new JButton(trans.get("btn.reset"));
652 resetButton.setToolTipText(trans.get("btn.reset.ttip"));
653 resetButton.addActionListener(new ActionListener() {
655 public void actionPerformed(ActionEvent e) {
656 log.user("Resetting optimization design");
660 disableComponents.add(resetButton);
661 panel.add(resetButton, "gapright para, right");
663 closeButton = new JButton(trans.get("btn.close"));
664 closeButton.setToolTipText(trans.get("btn.close.ttip"));
665 closeButton.addActionListener(new ActionListener() {
667 public void actionPerformed(ActionEvent e) {
668 log.user("Closing optimization dialog");
670 GeneralOptimizationDialog.this.dispose();
673 panel.add(closeButton, "right");
679 GUIUtil.setDisposableDialogOptions(this, null);
683 private void startOptimization() {
685 log.info("Optimization already running");
690 if (selectedModifiers.isEmpty()) {
691 JOptionPane.showMessageDialog(this, trans.get("error.selectParams.text"),
692 trans.get("error.selectParams.title"), JOptionPane.ERROR_MESSAGE);
694 startButton.setSelected(false);
695 startButton.setText(START_TEXT);
703 // Update the button status
705 startButton.setSelected(true);
706 startButton.setText(STOP_TEXT);
710 // Create a copy of the simulation (we're going to modify the original in the current thread)
711 Simulation simulation = getSelectedSimulation();
712 Rocket rocketCopy = simulation.getRocket().copyWithOriginalID();
713 simulation = simulation.duplicateSimulation(rocketCopy);
715 OptimizableParameter parameter = getSelectedParameter();
717 OptimizationGoal goal;
718 String value = (String) optimizationGoalCombo.getSelectedItem();
719 if (GOAL_MAXIMIZE.equals(value)) {
720 goal = new MaximizationGoal();
721 } else if (GOAL_MINIMIZE.equals(value)) {
722 goal = new MinimizationGoal();
723 } else if (GOAL_SEEK.equals(value)) {
724 goal = new ValueSeekGoal(optimizationSeekValue.getValue());
726 throw new BugException("optimizationGoalCombo had invalid value: " + value);
729 SimulationDomain domain;
730 if (minimumStabilitySelected.isSelected() || maximumStabilitySelected.isSelected()) {
732 boolean minAbsolute, maxAbsolute;
735 * Make minAbsolute/maxAbsolute consistent with each other to produce reasonable
736 * result in plot tool tips. Yes, this is a bit ugly.
740 Unit unit = minimumStability.getCurrentUnit();
741 if (unit instanceof CaliberUnit) {
742 min = unit.toUnit(minimumStability.getValue());
745 min = minimumStability.getValue();
750 unit = maximumStability.getCurrentUnit();
751 if (unit instanceof CaliberUnit) {
752 max = unit.toUnit(maximumStability.getValue());
755 max = maximumStability.getValue();
760 if (!minimumStabilitySelected.isSelected()) {
762 minAbsolute = maxAbsolute;
764 if (!maximumStabilitySelected.isSelected()) {
766 maxAbsolute = minAbsolute;
769 domain = new StabilityDomain(min, minAbsolute, max, maxAbsolute);
771 domain = new IdentitySimulationDomain();
774 SimulationModifier[] modifiers = selectedModifiers.toArray(new SimulationModifier[0]);
776 // Create and start the background worker
777 worker = new OptimizationWorker(simulation, parameter, goal, domain, modifiers) {
779 protected void done(OptimizationException exception) {
780 log.info("Optimization finished, exception=" + exception, exception);
782 if (exception != null) {
783 JOptionPane.showMessageDialog(GeneralOptimizationDialog.this,
785 trans.get("error.optimizationFailure.text"),
786 exception.getLocalizedMessage()
787 }, trans.get("error.optimizationFailure.title"), JOptionPane.ERROR_MESSAGE);
795 protected void functionEvaluated(List<FunctionEvaluationData> data) {
796 for (FunctionEvaluationData d : data) {
797 evaluationHistory.put(d.getPoint(), d);
804 protected void optimizationStepTaken(List<OptimizationStepData> data) {
806 // Add starting point to the path
807 if (optimizationPath.isEmpty()) {
808 optimizationPath.add(data.get(0).getOldPoint());
811 // Add other points to the path
812 for (OptimizationStepData d : data) {
813 optimizationPath.add(d.getNewPoint());
816 // Get function value from the latest point
817 OptimizationStepData latest = data.get(data.size() - 1);
818 Point newPoint = latest.getNewPoint();
820 FunctionEvaluationData pointValue = evaluationHistory.get(newPoint);
821 if (pointValue != null) {
822 bestValue = pointValue.getParameterValue().getValue();
824 log.error("History does not contain point " + newPoint);
825 bestValue = Double.NaN;
828 // Update the simulation
829 Simulation sim = getSelectedSimulation();
830 for (int i = 0; i < newPoint.dim(); i++) {
832 selectedModifiers.get(i).modify(sim, newPoint.get(i));
833 } catch (OptimizationException e) {
834 throw new BugException("Simulation modifier failed to modify the base simulation " +
835 "modifier=" + selectedModifiers.get(i), e);
838 figure.updateFigure();
840 // Update other counter data
841 stepCount += data.size();
842 stepSize = latest.getStepSize();
854 private void stopOptimization() {
856 log.info("Optimization not running");
860 if (worker != null && worker.isAlive()) {
861 log.info("Worker still running, interrupting it and setting to null");
869 // Update the button status
871 startButton.setSelected(false);
872 startButton.setText(START_TEXT);
883 * Reset the current optimization history and values. This does not reset the design.
885 private void clearHistory() {
886 evaluationHistory.clear();
887 optimizationPath.clear();
888 bestValue = Double.NaN;
889 bestValueUnit = getSelectedParameter().getUnitGroup().getDefaultUnit();
898 private void applyDesign() {
899 // TODO: MEDIUM: Apply also potential changes to simulations
900 Rocket src = getSelectedSimulation().getRocket().copyWithOriginalID();
901 Rocket dest = baseDocument.getRocket();
903 baseDocument.startUndo(trans.get("undoText"));
906 // Remove all children
907 while (dest.getChildCount() > 0) {
911 // Move all children to the destination rocket
912 while (src.getChildCount() > 0) {
913 RocketComponent c = src.getChild(0);
920 baseDocument.stopUndo();
925 private void resetDesign() {
928 documentCopy = baseDocument.copy();
930 loadOptimizationParameters();
931 loadSimulationModifiers();
933 // Replace selected modifiers with corresponding new modifiers
934 List<SimulationModifier> newSelected = new ArrayList<SimulationModifier>();
935 for (SimulationModifier original : selectedModifiers) {
936 List<SimulationModifier> newModifiers = simulationModifiers.get(original.getRelatedObject());
937 if (newModifiers != null) {
938 int index = newModifiers.indexOf(original);
940 newSelected.add(newModifiers.get(index));
944 selectedModifiers.clear();
945 selectedModifiers.addAll(newSelected);
946 selectedModifierTableModel.fireTableDataChanged();
948 // Update the available modifier tree
949 availableModifierTree.populateTree(documentCopy.getRocket(), simulationModifiers);
950 availableModifierTree.expandComponents();
953 // Update selectable simulations
954 populateSimulations();
956 // Update selectable parameters
957 populateParameters();
962 private void populateSimulations() {
963 String current = null;
964 Object selection = simulationSelectionCombo.getSelectedItem();
965 if (selection != null) {
966 current = selection.toString();
970 List<Named<Simulation>> simulations = new ArrayList<Named<Simulation>>();
971 Rocket rocket = documentCopy.getRocket();
973 for (Simulation s : documentCopy.getSimulations()) {
974 String id = s.getConfiguration().getMotorConfigurationID();
975 String name = createSimulationName(s.getName(), rocket.getMotorConfigurationNameOrDescription(id));
976 simulations.add(new Named<Simulation>(s, name));
979 for (String id : rocket.getMotorConfigurationIDs()) {
983 Simulation sim = new Simulation(rocket);
984 sim.getConfiguration().setMotorConfigurationID(id);
985 String name = createSimulationName(trans.get("basicSimulationName"), rocket.getMotorConfigurationNameOrDescription(id));
986 simulations.add(new Named<Simulation>(sim, name));
990 Simulation sim = new Simulation(rocket);
991 sim.getConfiguration().setMotorConfigurationID(null);
992 String name = createSimulationName(trans.get("noSimulationName"), rocket.getMotorConfigurationNameOrDescription(null));
993 simulations.add(new Named<Simulation>(sim, name));
996 simulationSelectionCombo.setModel(new DefaultComboBoxModel(simulations.toArray()));
998 if (current != null) {
999 for (int i = 0; i < simulations.size(); i++) {
1000 if (simulations.get(i).toString().equals(current)) {
1001 simulationSelectionCombo.setSelectedIndex(i);
1009 private void populateParameters() {
1010 String current = null;
1011 Object selection = optimizationParameterCombo.getSelectedItem();
1012 if (selection != null) {
1013 current = selection.toString();
1015 // Default to apogee altitude event if it is not the first one in the list
1016 current = trans.get("MaximumAltitudeParameter.name");
1019 List<Named<OptimizableParameter>> parameters = new ArrayList<Named<OptimizableParameter>>();
1020 for (OptimizableParameter p : optimizationParameters) {
1021 parameters.add(new Named<OptimizableParameter>(p, p.getName()));
1024 optimizationParameterCombo.setModel(new DefaultComboBoxModel(parameters.toArray()));
1026 for (int i = 0; i < parameters.size(); i++) {
1027 if (parameters.get(i).toString().equals(current)) {
1028 optimizationParameterCombo.setSelectedIndex(i);
1034 private void updateCounters() {
1035 bestValueLabel.setText(bestValueUnit.toStringUnit(bestValue));
1036 stepCountLabel.setText("" + stepCount);
1037 evaluationCountLabel.setText("" + evaluationCount);
1038 stepSizeLabel.setText(UnitGroup.UNITS_RELATIVE.toStringUnit(stepSize));
1042 private void loadOptimizationParameters() {
1043 optimizationParameters.clear();
1044 optimizationParameters.addAll(OptimizationServiceHelper.getOptimizableParameters(documentCopy));
1046 if (optimizationParameters.isEmpty()) {
1047 throw new BugException("No rocket optimization parameters found, distribution built wrong.");
1050 Collections.sort(optimizationParameters, new Comparator<OptimizableParameter>() {
1052 public int compare(OptimizableParameter o1, OptimizableParameter o2) {
1053 return o1.getName().compareTo(o2.getName());
1059 private void loadSimulationModifiers() {
1060 simulationModifiers.clear();
1062 for (SimulationModifier m : OptimizationServiceHelper.getSimulationModifiers(documentCopy)) {
1063 Object key = m.getRelatedObject();
1064 List<SimulationModifier> list = simulationModifiers.get(key);
1066 list = new ArrayList<SimulationModifier>();
1067 simulationModifiers.put(key, list);
1072 for (Object key : simulationModifiers.keySet()) {
1073 List<SimulationModifier> list = simulationModifiers.get(key);
1074 Collections.sort(list, new Comparator<SimulationModifier>() {
1076 public int compare(SimulationModifier o1, SimulationModifier o2) {
1077 return o1.getName().compareTo(o2.getName());
1086 private void addModifier(SimulationModifier mod) {
1087 if (!selectedModifiers.contains(mod)) {
1088 log.user(1, "Adding simulation modifier " + mod);
1089 selectedModifiers.add(mod);
1090 Collections.sort(selectedModifiers, new SimulationModifierComparator());
1091 selectedModifierTableModel.fireTableDataChanged();
1092 availableModifierTree.repaint();
1094 log.user(1, "Attempting to add an already existing simulation modifier " + mod);
1099 private void removeModifier(SimulationModifier mod) {
1100 log.user(1, "Removing simulation modifier " + mod);
1101 selectedModifiers.remove(mod);
1102 selectedModifierTableModel.fireTableDataChanged();
1103 availableModifierTree.repaint();
1109 * Update the enabled status of all components in the dialog.
1111 private void updateComponents() {
1120 // First enable all components if optimization not running
1122 for (JComponent c : disableComponents) {
1129 SimulationModifier mod = getSelectedAvailableModifier();
1130 if (mod != null && !selectedModifiers.contains(mod)) {
1131 addButton.setEnabled(true);
1133 addButton.setEnabled(false);
1137 removeButton.setEnabled(selectedModifierTable.getSelectedRow() >= 0);
1139 // "Remove all" button
1140 removeAllButton.setEnabled(!selectedModifiers.isEmpty());
1143 // Optimization goal
1144 String selected = (String) optimizationGoalCombo.getSelectedItem();
1145 if (GOAL_SEEK.equals(selected)) {
1146 optimizationGoalSpinner.setVisible(true);
1147 optimizationGoalUnitSelector.setVisible(true);
1149 optimizationGoalSpinner.setVisible(false);
1150 optimizationGoalUnitSelector.setVisible(false);
1154 // Minimum/maximum stability options
1155 minimumStabilitySpinner.setEnabled(minimumStabilitySelected.isSelected());
1156 minimumStabilityUnitSelector.setEnabled(minimumStabilitySelected.isSelected());
1157 maximumStabilitySpinner.setEnabled(maximumStabilitySelected.isSelected());
1158 maximumStabilityUnitSelector.setEnabled(maximumStabilitySelected.isSelected());
1161 // Plot button (enabled if path exists and dimensionality is 1 or 2)
1162 plotButton.setEnabled(!optimizationPath.isEmpty() && (selectedModifiers.size() == 1 || selectedModifiers.size() == 2));
1164 // Save button (enabled if path exists)
1165 saveButton.setEnabled(!optimizationPath.isEmpty());
1168 // Last disable all components if optimization is running
1170 for (JComponent c : disableComponents) {
1171 c.setEnabled(false);
1176 // Update description text
1177 mod = getSelectedModifier();
1179 selectedModifierDescription.setText(mod.getDescription());
1181 selectedModifierDescription.setText("");
1185 // Update the figure
1186 figure.setConfiguration(getSelectedSimulation().getConfiguration());
1192 private void savePath() {
1194 if (evaluationHistory.isEmpty()) {
1195 throw new BugException("evaluation history is empty");
1198 CsvOptionPanel csvOptions = new CsvOptionPanel(GeneralOptimizationDialog.class,
1199 trans.get("export.header"), trans.get("export.header.ttip"));
1202 JFileChooser chooser = new JFileChooser();
1203 chooser.setFileFilter(FileHelper.CSV_FILE_FILTER);
1204 chooser.setCurrentDirectory(Prefs.getDefaultDirectory());
1205 chooser.setAccessory(csvOptions);
1207 if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION)
1210 File file = chooser.getSelectedFile();
1214 file = FileHelper.ensureExtension(file, "csv");
1215 if (!FileHelper.confirmWrite(file, this)) {
1219 String fieldSeparator = csvOptions.getFieldSeparator();
1220 String commentCharacter = csvOptions.getCommentCharacter();
1221 boolean includeHeader = csvOptions.getSelectionOption(0);
1222 csvOptions.storePreferences();
1224 log.info("Saving optimization path to " + file + ", fieldSeparator=" + fieldSeparator +
1225 ", commentCharacter=" + commentCharacter + ", includeHeader=" + includeHeader);
1228 Writer writer = new BufferedWriter(new FileWriter(file));
1231 if (includeHeader) {
1232 FunctionEvaluationData data = evaluationHistory.values().iterator().next();
1234 writer.write(commentCharacter);
1235 for (SimulationModifier mod : selectedModifiers) {
1236 writer.write(mod.getRelatedObject().toString() + ": " + mod.getName() + " / " +
1237 mod.getUnitGroup().getDefaultUnit().getUnit());
1238 writer.write(fieldSeparator);
1240 if (minimumStabilitySelected.isSelected() || maximumStabilitySelected.isSelected()) {
1241 writer.write(trans.get("export.stability") + " / " + data.getDomainReference().getUnit().getUnit());
1242 writer.write(fieldSeparator);
1244 writer.write(getSelectedParameter().getName() + " / " +
1245 getSelectedParameter().getUnitGroup().getDefaultUnit().getUnit());
1251 for (FunctionEvaluationData data : evaluationHistory.values()) {
1252 Value[] state = data.getState();
1254 for (int i = 0; i < state.length; i++) {
1255 writer.write(TextUtil.doubleToString(state[i].getUnitValue()));
1256 writer.write(fieldSeparator);
1259 if (minimumStabilitySelected.isSelected() || maximumStabilitySelected.isSelected()) {
1260 writer.write(TextUtil.doubleToString(data.getDomainReference().getUnitValue()));
1261 writer.write(fieldSeparator);
1264 if (data.getParameterValue() != null) {
1265 writer.write(TextUtil.doubleToString(data.getParameterValue().getUnitValue()));
1267 writer.write("N/A");
1273 log.info("File successfully saved");
1275 } catch (IOException e) {
1276 FileHelper.errorWriting(e, this);
1282 * Return the currently selected available simulation modifier from the modifier tree,
1283 * or <code>null</code> if none selected.
1285 private SimulationModifier getSelectedAvailableModifier() {
1286 TreePath treepath = availableModifierTree.getSelectionPath();
1287 if (treepath != null) {
1288 Object o = ((DefaultMutableTreeNode) treepath.getLastPathComponent()).getUserObject();
1289 if (o instanceof SimulationModifier) {
1290 return (SimulationModifier) o;
1297 * Return the currently selected simulation.
1298 * @return the selected simulation.
1300 @SuppressWarnings("unchecked")
1301 private Simulation getSelectedSimulation() {
1302 return ((Named<Simulation>) simulationSelectionCombo.getSelectedItem()).get();
1307 * Return the currently selected simulation modifier from the table,
1308 * or <code>null</code> if none selected.
1309 * @return the selected modifier or <code>null</code>.
1311 private SimulationModifier getSelectedModifier() {
1312 int row = selectedModifierTable.getSelectedRow();
1316 row = selectedModifierTable.convertRowIndexToModel(row);
1317 return selectedModifiers.get(row);
1322 * Return the currently selected optimization parameter.
1323 * @return the selected optimization parameter.
1325 @SuppressWarnings("unchecked")
1326 private OptimizableParameter getSelectedParameter() {
1327 return ((Named<OptimizableParameter>) optimizationParameterCombo.getSelectedItem()).get();
1331 private Unit getModifierUnit(int index) {
1332 return selectedModifiers.get(index).getUnitGroup().getDefaultUnit();
1335 private String createSimulationName(String simulationName, String motorConfiguration) {
1337 boolean hasParenthesis = motorConfiguration.matches("^[\\[\\(].*[\\]\\)]$");
1338 name = simulationName + " ";
1339 if (!hasParenthesis) {
1342 name += motorConfiguration;
1343 if (!hasParenthesis) {
1350 * The table model for the parameter selection.
1352 * [Body tube: Length] [min] [max] [unit]
1354 private class ParameterSelectionTableModel extends AbstractTableModel {
1356 private static final int PARAMETER = 0;
1357 private static final int CURRENT = 1;
1358 private static final int MIN = 2;
1359 private static final int MAX = 3;
1360 private static final int COUNT = 4;
1363 public int getColumnCount() {
1368 public int getRowCount() {
1369 return selectedModifiers.size();
1373 public String getColumnName(int column) {
1376 return trans.get("table.col.parameter");
1378 return trans.get("table.col.current");
1380 return trans.get("table.col.min");
1382 return trans.get("table.col.max");
1384 throw new IndexOutOfBoundsException("column=" + column);
1390 public Class<?> getColumnClass(int column) {
1393 return String.class;
1395 return Double.class;
1397 return Double.class;
1399 return Double.class;
1401 throw new IndexOutOfBoundsException("column=" + column);
1406 public Object getValueAt(int row, int column) {
1408 SimulationModifier modifier = selectedModifiers.get(row);
1412 return modifier.getRelatedObject().toString() + ": " + modifier.getName();
1415 return getModifierUnit(row).toUnit(modifier.getCurrentSIValue(getSelectedSimulation()));
1416 } catch (OptimizationException e) {
1417 throw new BugException("Could not read current SI value from modifier " + modifier, e);
1420 return getModifierUnit(row).toUnit(modifier.getMinValue());
1422 return getModifierUnit(row).toUnit(modifier.getMaxValue());
1424 throw new IndexOutOfBoundsException("column=" + column);
1430 public void setValueAt(Object value, int row, int column) {
1437 double min = (Double) value;
1438 min = getModifierUnit(row).fromUnit(min);
1439 selectedModifiers.get(row).setMinValue(min);
1446 double max = (Double) value;
1447 max = getModifierUnit(row).fromUnit(max);
1448 selectedModifiers.get(row).setMaxValue(max);
1452 throw new IndexOutOfBoundsException("column=" + column);
1454 this.fireTableRowsUpdated(row, row);
1459 public boolean isCellEditable(int row, int column) {
1470 throw new IndexOutOfBoundsException("column=" + column);
1477 private class DoubleCellRenderer extends DefaultTableCellRenderer {
1479 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
1480 boolean hasFocus, int row, int column) {
1482 super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
1484 double val = (Double) value;
1485 Unit unit = getModifierUnit(row);
1487 val = unit.fromUnit(val);
1488 this.setText(unit.toStringUnit(val));
1495 private static class SimulationModifierComparator implements Comparator<SimulationModifier> {
1498 public int compare(SimulationModifier mod1, SimulationModifier mod2) {
1499 Object rel1 = mod1.getRelatedObject();
1500 Object rel2 = mod2.getRelatedObject();
1503 * Primarily order by related object:
1505 * - RocketComponents first
1506 * - Two RocketComponents are ordered based on their position in the rocket
1508 if (!rel1.equals(rel2)) {
1510 if (rel1 instanceof RocketComponent) {
1511 if (rel2 instanceof RocketComponent) {
1513 RocketComponent root = ((RocketComponent) rel1).getRoot();
1514 for (RocketComponent c : root) {
1515 if (c.equals(rel1)) {
1518 if (c.equals(rel2)) {
1523 throw new BugException("Error sorting modifiers, mod1=" + mod1 + " rel1=" + rel1 +
1524 " mod2=" + mod2 + " rel2=" + rel2);
1530 if (rel2 instanceof RocketComponent) {
1537 // Secondarily sort by name
1538 return collator.compare(mod1.getName(), mod2.getName());