1 package net.sf.openrocket.gui.dialogs.optimization;
3 import java.util.ArrayList;
5 import java.util.concurrent.LinkedBlockingQueue;
7 import javax.swing.SwingUtilities;
9 import net.sf.openrocket.document.Simulation;
10 import net.sf.openrocket.logging.LogHelper;
11 import net.sf.openrocket.optimization.general.FunctionOptimizer;
12 import net.sf.openrocket.optimization.general.OptimizationController;
13 import net.sf.openrocket.optimization.general.OptimizationException;
14 import net.sf.openrocket.optimization.general.ParallelExecutorCache;
15 import net.sf.openrocket.optimization.general.ParallelFunctionCache;
16 import net.sf.openrocket.optimization.general.Point;
17 import net.sf.openrocket.optimization.general.multidim.MultidirectionalSearchOptimizer;
18 import net.sf.openrocket.optimization.rocketoptimization.OptimizableParameter;
19 import net.sf.openrocket.optimization.rocketoptimization.OptimizationGoal;
20 import net.sf.openrocket.optimization.rocketoptimization.RocketOptimizationFunction;
21 import net.sf.openrocket.optimization.rocketoptimization.RocketOptimizationListener;
22 import net.sf.openrocket.optimization.rocketoptimization.SimulationDomain;
23 import net.sf.openrocket.optimization.rocketoptimization.SimulationModifier;
24 import net.sf.openrocket.startup.Application;
25 import net.sf.openrocket.unit.Value;
26 import net.sf.openrocket.util.BugException;
29 * A background worker that runs the optimization in the background. It supports providing
30 * evaluation and step counter information via listeners that are executed on the EDT.
32 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
34 public abstract class OptimizationWorker extends Thread implements OptimizationController, RocketOptimizationListener {
37 * Note: This is implemented as a separate Thread object instead of a SwingWorker because
38 * the SwingWorker cannot be interrupted in any way except by canceling the task, which
39 * makes it impossible to wait for its exiting (SwingWorker.get() throws a CancellationException
40 * if cancel() has been called).
42 * SwingWorker also seems to miss some chunks that have been provided to process() when the
45 * Nothing of this is documented, of course...
48 private static final LogHelper log = Application.getLogger();
50 /** Notify listeners every this many milliseconds */
51 private static final long PURGE_TIMEOUT = 500;
52 /** End optimization when step size is below this threshold */
53 private static final double STEP_SIZE_LIMIT = 0.005;
55 private final FunctionOptimizer optimizer;
56 private final RocketOptimizationFunction function;
58 private final Simulation simulation;
59 private final SimulationModifier[] modifiers;
61 private final ParallelFunctionCache cache;
64 private final LinkedBlockingQueue<FunctionEvaluationData> evaluationQueue =
65 new LinkedBlockingQueue<FunctionEvaluationData>();
66 private final LinkedBlockingQueue<OptimizationStepData> stepQueue =
67 new LinkedBlockingQueue<OptimizationStepData>();
68 private volatile long lastPurge = 0;
70 private OptimizationException optimizationException = null;
75 * @param simulation the simulation
76 * @param parameter the optimization parameter
77 * @param goal the optimization goal
78 * @param domain the optimization domain
79 * @param modifiers the simulation modifiers
81 public OptimizationWorker(Simulation simulation, OptimizableParameter parameter,
82 OptimizationGoal goal, SimulationDomain domain, SimulationModifier... modifiers) {
84 this.simulation = simulation;
85 this.modifiers = modifiers.clone();
87 function = new RocketOptimizationFunction(simulation, parameter, goal, domain, modifiers);
88 function.addRocketOptimizationListener(this);
90 cache = new ParallelExecutorCache(1);
91 cache.setFunction(function);
93 optimizer = new MultidirectionalSearchOptimizer(cache);
101 double[] current = new double[modifiers.length];
102 for (int i = 0; i < modifiers.length; i++) {
103 current[i] = modifiers[i].getCurrentScaledValue(simulation);
105 Point initial = new Point(current);
107 optimizer.optimize(initial, this);
109 } catch (OptimizationException e) {
110 this.optimizationException = e;
112 SwingUtilities.invokeLater(new Runnable() {
115 lastPurge = System.currentTimeMillis() + 24L * 3600L * 1000L;
117 done(optimizationException);
124 * This method is called after the optimization has ended, either normally, when interrupted
125 * or by throwing an exception. This method is called on the EDT, like the done() method of SwingWorker.
127 * All data chunks to the listeners will be guaranteed to have been processed before calling done().
129 * @param exception a possible optimization exception that occurred, or <code>null</code> for normal exit.
131 protected abstract void done(OptimizationException exception);
135 * This method is called for each function evaluation that has taken place.
136 * This method is called on the EDT.
138 * @param data the data accumulated since the last call
140 protected abstract void functionEvaluated(List<FunctionEvaluationData> data);
143 * This method is called after each step taken by the optimization algorithm.
144 * This method is called on the EDT.
146 * @param data the data accumulated since the last call
148 protected abstract void optimizationStepTaken(List<OptimizationStepData> data);
152 * Publishes data to the listeners. The queue is purged every PURGE_TIMEOUT milliseconds.
154 * @param data the data to publish to the listeners
156 private synchronized void publish(FunctionEvaluationData evaluation, OptimizationStepData step) {
158 if (evaluation != null) {
159 evaluationQueue.add(evaluation);
165 // Add a method to the EDT to process the queue data
166 long now = System.currentTimeMillis();
167 if (lastPurge + PURGE_TIMEOUT <= now) {
169 SwingUtilities.invokeLater(new Runnable() {
181 * Process the queue and call the listeners. This method must always be called from the EDT.
183 private void processQueue() {
185 if (!SwingUtilities.isEventDispatchThread()) {
186 throw new BugException("processQueue called from non-EDT");
190 List<FunctionEvaluationData> evaluations = new ArrayList<FunctionEvaluationData>();
191 evaluationQueue.drainTo(evaluations);
192 if (!evaluations.isEmpty()) {
193 functionEvaluated(evaluations);
197 List<OptimizationStepData> steps = new ArrayList<OptimizationStepData>();
198 stepQueue.drainTo(steps);
199 if (!steps.isEmpty()) {
200 optimizationStepTaken(steps);
208 * NOTE: The stepTaken and evaluated methods may be called from other
209 * threads than the EDT or the SwingWorker thread!
213 public boolean stepTaken(Point oldPoint, double oldValue, Point newPoint, double newValue, double stepSize) {
214 publish(null, new OptimizationStepData(oldPoint, oldValue, newPoint, newValue, stepSize));
216 if (stepSize < STEP_SIZE_LIMIT) {
217 log.info("stepSize=" + stepSize + " is below limit, ending optimization");
225 public void evaluated(Point point, Value[] state, Value domainReference, Value parameterValue, double goalValue) {
226 publish(new FunctionEvaluationData(point, state, domainReference, parameterValue, goalValue), null);