optimization updates
This commit is contained in:
parent
9a8e702df2
commit
2639d13391
@ -1,3 +1,8 @@
|
||||
2011-08-08 Sampo Niskanen
|
||||
|
||||
* Enhanced one-dimensional optimization algorithm
|
||||
* [BUG] l10n/ directory not included in source distribution
|
||||
|
||||
2011-08-07 Sampo Niskanen
|
||||
|
||||
* Optimization implementation
|
||||
|
@ -89,7 +89,7 @@
|
||||
<fileset dir="." includes="*">
|
||||
<type type="file"/>
|
||||
</fileset>
|
||||
<fileset dir="." includes="datafiles/ lib/ lib-test/ pix/ src/ test/"/>
|
||||
<fileset dir="." includes="datafiles/ lib/ lib-test/ pix/ src/ test/ l10n/"/>
|
||||
</copy>
|
||||
<zip destfile="${dist.src}" basedir="${build.dir}" includes="${pkgname}/"/>
|
||||
<delete dir="${build.dir}/${pkgname}"/>
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.sf.openrocket.gui.components;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.text.ParseException;
|
||||
|
||||
import javax.swing.AbstractCellEditor;
|
||||
import javax.swing.JSpinner;
|
||||
@ -34,6 +35,17 @@ public class DoubleCellEditor extends AbstractCellEditor implements TableCellEdi
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean stopCellEditing() {
|
||||
try {
|
||||
editor.commitEdit();
|
||||
} catch (ParseException e) {
|
||||
// Ignore
|
||||
}
|
||||
return super.stopCellEditing();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object getCellEditorValue() {
|
||||
return model.getValue();
|
||||
|
@ -95,6 +95,8 @@ import net.sf.openrocket.util.TextUtil;
|
||||
|
||||
import com.itextpdf.text.Font;
|
||||
|
||||
// FIXME: Override to zero mass produces NaN in simulation
|
||||
|
||||
/**
|
||||
* General rocket optimization dialog.
|
||||
*
|
||||
@ -937,7 +939,10 @@ public class GeneralOptimizationDialog extends JDialog {
|
||||
if (newModifiers != null) {
|
||||
int index = newModifiers.indexOf(original);
|
||||
if (index >= 0) {
|
||||
newSelected.add(newModifiers.get(index));
|
||||
SimulationModifier updated = newModifiers.get(index);
|
||||
updated.setMinValue(original.getMinValue());
|
||||
updated.setMaxValue(original.getMaxValue());
|
||||
newSelected.add(updated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import net.sf.openrocket.optimization.general.ParallelExecutorCache;
|
||||
import net.sf.openrocket.optimization.general.ParallelFunctionCache;
|
||||
import net.sf.openrocket.optimization.general.Point;
|
||||
import net.sf.openrocket.optimization.general.multidim.MultidirectionalSearchOptimizer;
|
||||
import net.sf.openrocket.optimization.general.onedim.GoldenSectionSearchOptimizer;
|
||||
import net.sf.openrocket.optimization.rocketoptimization.OptimizableParameter;
|
||||
import net.sf.openrocket.optimization.rocketoptimization.OptimizationGoal;
|
||||
import net.sf.openrocket.optimization.rocketoptimization.RocketOptimizationFunction;
|
||||
@ -90,7 +91,11 @@ public abstract class OptimizationWorker extends Thread implements OptimizationC
|
||||
cache = new ParallelExecutorCache(1);
|
||||
cache.setFunction(function);
|
||||
|
||||
optimizer = new MultidirectionalSearchOptimizer(cache);
|
||||
if (modifiers.length == 1) {
|
||||
optimizer = new GoldenSectionSearchOptimizer(cache);
|
||||
} else {
|
||||
optimizer = new MultidirectionalSearchOptimizer(cache);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -3,6 +3,7 @@ package net.sf.openrocket.optimization.general;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
@ -201,6 +202,30 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void abortAll() {
|
||||
Iterator<Point> iterator = futureMap.keySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Point point = iterator.next();
|
||||
Future<Double> future = futureMap.get(point);
|
||||
iterator.remove();
|
||||
|
||||
if (future.isDone()) {
|
||||
// Evaluation has been completed, store value in cache
|
||||
try {
|
||||
double value = future.get();
|
||||
functionCache.put(point, value);
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
} else {
|
||||
// Cancel the evaluation
|
||||
future.cancel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public double getValue(Point point) {
|
||||
if (isOutsideRange(point)) {
|
||||
@ -274,4 +299,5 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -68,4 +68,10 @@ public interface ParallelFunctionCache extends FunctionCache {
|
||||
* @return <code>true</code> if the point has been computed anyway, <code>false</code> if not.
|
||||
*/
|
||||
public boolean abort(Point point);
|
||||
|
||||
|
||||
/**
|
||||
* Abort the computation of all still unexecuted points.
|
||||
*/
|
||||
public void abortAll();
|
||||
}
|
||||
|
@ -215,6 +215,7 @@ public class MultidirectionalSearchOptimizer implements FunctionOptimizer, Stati
|
||||
|
||||
log.info("Finishing optimization at point " + simplex.get(0) + " value = " +
|
||||
functionExecutor.getValue(simplex.get(0)));
|
||||
log.info("Optimization statistics: " + getStatistics());
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,275 @@
|
||||
package net.sf.openrocket.optimization.general.onedim;
|
||||
|
||||
import net.sf.openrocket.logging.LogHelper;
|
||||
import net.sf.openrocket.optimization.general.FunctionCache;
|
||||
import net.sf.openrocket.optimization.general.FunctionOptimizer;
|
||||
import net.sf.openrocket.optimization.general.OptimizationController;
|
||||
import net.sf.openrocket.optimization.general.OptimizationException;
|
||||
import net.sf.openrocket.optimization.general.ParallelFunctionCache;
|
||||
import net.sf.openrocket.optimization.general.Point;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
import net.sf.openrocket.util.Statistics;
|
||||
|
||||
/**
|
||||
* An implementation of a one-dimensional golden section search method
|
||||
* (see e.g. Nonlinear programming, Bazaraa, Sherall, Shetty, 2nd edition, p. 270)
|
||||
* <p>
|
||||
* This implementation attempts to guess future evaluations and computes them in parallel
|
||||
* with the next point.
|
||||
* <p>
|
||||
* The optimization can be aborted by interrupting the current thread.
|
||||
*/
|
||||
public class GoldenSectionSearchOptimizer implements FunctionOptimizer, Statistics {
|
||||
private static final LogHelper log = Application.getLogger();
|
||||
|
||||
private static final double ALPHA = (Math.sqrt(5) - 1) / 2.0;
|
||||
|
||||
|
||||
private ParallelFunctionCache functionExecutor;
|
||||
|
||||
private Point current = null;
|
||||
|
||||
private int guessSuccess = 0;
|
||||
private int guessFailure = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Construct an optimizer with no function executor.
|
||||
*/
|
||||
public GoldenSectionSearchOptimizer() {
|
||||
// No-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an optimizer.
|
||||
*
|
||||
* @param functionExecutor the function executor.
|
||||
*/
|
||||
public GoldenSectionSearchOptimizer(ParallelFunctionCache functionExecutor) {
|
||||
super();
|
||||
this.functionExecutor = functionExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void optimize(Point initial, OptimizationController control) throws OptimizationException {
|
||||
|
||||
if (initial.dim() != 1) {
|
||||
throw new IllegalArgumentException("Only single-dimensional optimization supported, dim=" +
|
||||
initial.dim());
|
||||
}
|
||||
|
||||
log.info("Starting golden section search for optimization");
|
||||
|
||||
Point guessAC = null;
|
||||
Point guessBD = null;
|
||||
|
||||
try {
|
||||
boolean guessedAC;
|
||||
|
||||
Point previous = p(0);
|
||||
double previousValue = Double.NaN;
|
||||
current = previous;
|
||||
double currentValue = Double.NaN;
|
||||
|
||||
/*
|
||||
* Initialize the points + computation.
|
||||
*/
|
||||
Point a = p(0);
|
||||
Point d = p(1.0);
|
||||
Point b = section1(a, d);
|
||||
Point c = section2(a, d);
|
||||
|
||||
functionExecutor.compute(a);
|
||||
functionExecutor.compute(d);
|
||||
functionExecutor.compute(b);
|
||||
functionExecutor.compute(c);
|
||||
|
||||
// Wait for points a and d, which normally are already precomputed
|
||||
functionExecutor.waitFor(a);
|
||||
functionExecutor.waitFor(d);
|
||||
|
||||
boolean continueOptimization = true;
|
||||
while (continueOptimization) {
|
||||
|
||||
/*
|
||||
* Get values at A & D for guessing.
|
||||
* These are pre-calculated except during the first step.
|
||||
*/
|
||||
double fa, fd;
|
||||
fa = functionExecutor.getValue(a);
|
||||
fd = functionExecutor.getValue(d);
|
||||
|
||||
|
||||
/*
|
||||
* Start calculating possible two next points. The order of evaluation
|
||||
* is selected based on the function values at A and D.
|
||||
*/
|
||||
guessAC = section1(a, c);
|
||||
guessBD = section2(b, d);
|
||||
System.err.println("Queueing " + guessAC + " and " + guessBD);
|
||||
if (Double.isNaN(fd) || fa < fd) {
|
||||
guessedAC = true;
|
||||
functionExecutor.compute(guessAC);
|
||||
functionExecutor.compute(guessBD);
|
||||
} else {
|
||||
guessedAC = false;
|
||||
functionExecutor.compute(guessBD);
|
||||
functionExecutor.compute(guessAC);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get values at B and C.
|
||||
*/
|
||||
double fb, fc;
|
||||
functionExecutor.waitFor(b);
|
||||
functionExecutor.waitFor(c);
|
||||
fb = functionExecutor.getValue(b);
|
||||
fc = functionExecutor.getValue(c);
|
||||
|
||||
double min = MathUtil.min(fa, fb, fc, fd);
|
||||
if (Double.isNaN(min)) {
|
||||
throw new OptimizationException("Unable to compute initial function values");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Update previous and current values for step control.
|
||||
*/
|
||||
previousValue = currentValue;
|
||||
currentValue = min;
|
||||
previous = current;
|
||||
if (min == fa) {
|
||||
current = a;
|
||||
} else if (min == fb) {
|
||||
current = b;
|
||||
} else if (min == fc) {
|
||||
current = c;
|
||||
} else {
|
||||
current = d;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Select next positions. These are already being calculated in the background
|
||||
* as guessAC and guessBD.
|
||||
*/
|
||||
if (min == fa || min == fb) {
|
||||
d = c;
|
||||
c = b;
|
||||
b = guessAC;
|
||||
functionExecutor.abort(guessBD);
|
||||
guessBD = null;
|
||||
log.debug("Selecting A-C region, a=" + a.get(0) + " c=" + c.get(0));
|
||||
if (guessedAC) {
|
||||
guessSuccess++;
|
||||
} else {
|
||||
guessFailure++;
|
||||
}
|
||||
} else {
|
||||
a = b;
|
||||
b = c;
|
||||
c = guessBD;
|
||||
functionExecutor.abort(guessAC);
|
||||
guessAC = null;
|
||||
log.debug("Selecting B-D region, b=" + b.get(0) + " d=" + d.get(0));
|
||||
if (!guessedAC) {
|
||||
guessSuccess++;
|
||||
} else {
|
||||
guessFailure++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check optimization control.
|
||||
*/
|
||||
continueOptimization = control.stepTaken(previous, previousValue,
|
||||
current, currentValue, c.get(0) - a.get(0));
|
||||
|
||||
if (Thread.interrupted()) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
log.info("Optimization was interrupted with InterruptedException");
|
||||
}
|
||||
|
||||
if (guessAC != null) {
|
||||
System.err.println("Aborting " + guessAC);
|
||||
functionExecutor.abort(guessAC);
|
||||
}
|
||||
if (guessBD != null) {
|
||||
System.err.println("Aborting " + guessBD);
|
||||
functionExecutor.abort(guessBD);
|
||||
}
|
||||
|
||||
|
||||
log.info("Finishing optimization at point " + getOptimumPoint() + " value " + getOptimumValue());
|
||||
log.info("Optimization statistics: " + getStatistics());
|
||||
}
|
||||
|
||||
|
||||
private Point p(double v) {
|
||||
return new Point(v);
|
||||
}
|
||||
|
||||
|
||||
private Point section1(Point a, Point b) {
|
||||
double va = a.get(0);
|
||||
double vb = b.get(0);
|
||||
return p(va + (1 - ALPHA) * (vb - va));
|
||||
}
|
||||
|
||||
private Point section2(Point a, Point b) {
|
||||
double va = a.get(0);
|
||||
double vb = b.get(0);
|
||||
return p(va + ALPHA * (vb - va));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Point getOptimumPoint() {
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public double getOptimumValue() {
|
||||
if (getOptimumPoint() == null) {
|
||||
return Double.NaN;
|
||||
}
|
||||
return functionExecutor.getValue(getOptimumPoint());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionCache getFunctionCache() {
|
||||
return functionExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFunctionCache(FunctionCache functionCache) {
|
||||
if (!(functionCache instanceof ParallelFunctionCache)) {
|
||||
throw new IllegalArgumentException("Function cache needs to be a ParallelFunctionCache: " + functionCache);
|
||||
}
|
||||
this.functionExecutor = (ParallelFunctionCache) functionCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatistics() {
|
||||
return String.format("Guess hit rate %d/%d = %.3f", guessSuccess, guessSuccess + guessFailure,
|
||||
((double) guessSuccess) / (guessSuccess + guessFailure));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetStatistics() {
|
||||
guessSuccess = 0;
|
||||
guessFailure = 0;
|
||||
}
|
||||
|
||||
}
|
@ -29,7 +29,7 @@ public interface OptimizableParameter {
|
||||
* @return the parameter value (any double value)
|
||||
* @throws OptimizationException if an error occurs preventing the optimization from continuing
|
||||
*/
|
||||
public double computeValue(Simulation simulation) throws OptimizationException;
|
||||
public double computeValue(Simulation simulation) throws OptimizationException, InterruptedException;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -7,8 +7,10 @@ import net.sf.openrocket.optimization.general.OptimizationException;
|
||||
import net.sf.openrocket.optimization.rocketoptimization.OptimizableParameter;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.exception.MotorIgnitionException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationCancelledException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationLaunchException;
|
||||
import net.sf.openrocket.simulation.listeners.system.InterruptListener;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
|
||||
@ -28,10 +30,10 @@ public class GroundHitVelocityParameter implements OptimizableParameter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double computeValue(Simulation simulation) throws OptimizationException {
|
||||
public double computeValue(Simulation simulation) throws OptimizationException, InterruptedException {
|
||||
try {
|
||||
log.debug("Running simulation to evaluate ground hit speed");
|
||||
simulation.simulate();
|
||||
simulation.simulate(new InterruptListener());
|
||||
double value = simulation.getSimulatedData().getBranch(0).getLast(FlightDataType.TYPE_VELOCITY_TOTAL);
|
||||
log.debug("Ground hit speed was " + value);
|
||||
return value;
|
||||
@ -41,6 +43,8 @@ public class GroundHitVelocityParameter implements OptimizableParameter {
|
||||
} catch (SimulationLaunchException e) {
|
||||
// Other launch exceptions result in zero altitude
|
||||
return Double.NaN;
|
||||
} catch (SimulationCancelledException e) {
|
||||
throw (InterruptedException) new InterruptedException("Optimization was interrupted").initCause(e);
|
||||
} catch (SimulationException e) {
|
||||
// Other exceptions fail
|
||||
throw new OptimizationException(e);
|
||||
|
@ -7,8 +7,10 @@ import net.sf.openrocket.optimization.general.OptimizationException;
|
||||
import net.sf.openrocket.optimization.rocketoptimization.OptimizableParameter;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.exception.MotorIgnitionException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationCancelledException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationLaunchException;
|
||||
import net.sf.openrocket.simulation.listeners.system.InterruptListener;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
|
||||
@ -28,10 +30,10 @@ public class LandingDistanceParameter implements OptimizableParameter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double computeValue(Simulation simulation) throws OptimizationException {
|
||||
public double computeValue(Simulation simulation) throws OptimizationException, InterruptedException {
|
||||
try {
|
||||
log.debug("Running simulation to evaluate rocket landing distance");
|
||||
simulation.simulate();
|
||||
simulation.simulate(new InterruptListener());
|
||||
double value = simulation.getSimulatedData().getBranch(0).getLast(FlightDataType.TYPE_POSITION_XY);
|
||||
log.debug("Landing distance was " + value);
|
||||
return value;
|
||||
@ -41,6 +43,8 @@ public class LandingDistanceParameter implements OptimizableParameter {
|
||||
} catch (SimulationLaunchException e) {
|
||||
// Other launch exceptions result in zero altitude
|
||||
return Double.NaN;
|
||||
} catch (SimulationCancelledException e) {
|
||||
throw (InterruptedException) new InterruptedException("Optimization was interrupted").initCause(e);
|
||||
} catch (SimulationException e) {
|
||||
// Other exceptions fail
|
||||
throw new OptimizationException(e);
|
||||
|
@ -7,9 +7,11 @@ import net.sf.openrocket.optimization.general.OptimizationException;
|
||||
import net.sf.openrocket.optimization.rocketoptimization.OptimizableParameter;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.exception.MotorIgnitionException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationCancelledException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationLaunchException;
|
||||
import net.sf.openrocket.simulation.listeners.system.ApogeeEndListener;
|
||||
import net.sf.openrocket.simulation.listeners.system.InterruptListener;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
|
||||
@ -29,10 +31,10 @@ public class MaximumAccelerationParameter implements OptimizableParameter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double computeValue(Simulation simulation) throws OptimizationException {
|
||||
public double computeValue(Simulation simulation) throws OptimizationException, InterruptedException {
|
||||
try {
|
||||
log.debug("Running simulation to evaluate maximum acceleration");
|
||||
simulation.simulate(new ApogeeEndListener());
|
||||
simulation.simulate(new ApogeeEndListener(), new InterruptListener());
|
||||
double value = simulation.getSimulatedData().getBranch(0).getMaximum(FlightDataType.TYPE_ACCELERATION_TOTAL);
|
||||
log.debug("Maximum acceleration was " + value);
|
||||
return value;
|
||||
@ -42,6 +44,8 @@ public class MaximumAccelerationParameter implements OptimizableParameter {
|
||||
} catch (SimulationLaunchException e) {
|
||||
// Other launch exceptions result in zero velocity
|
||||
return Double.NaN;
|
||||
} catch (SimulationCancelledException e) {
|
||||
throw (InterruptedException) new InterruptedException("Optimization was interrupted").initCause(e);
|
||||
} catch (SimulationException e) {
|
||||
// Other exceptions fail
|
||||
throw new OptimizationException(e);
|
||||
|
@ -7,9 +7,11 @@ import net.sf.openrocket.optimization.general.OptimizationException;
|
||||
import net.sf.openrocket.optimization.rocketoptimization.OptimizableParameter;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.exception.MotorIgnitionException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationCancelledException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationLaunchException;
|
||||
import net.sf.openrocket.simulation.listeners.system.ApogeeEndListener;
|
||||
import net.sf.openrocket.simulation.listeners.system.InterruptListener;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
|
||||
@ -29,10 +31,10 @@ public class MaximumAltitudeParameter implements OptimizableParameter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double computeValue(Simulation simulation) throws OptimizationException {
|
||||
public double computeValue(Simulation simulation) throws OptimizationException, InterruptedException {
|
||||
try {
|
||||
log.debug("Running simulation to evaluate apogee altitude");
|
||||
simulation.simulate(new ApogeeEndListener());
|
||||
simulation.simulate(new ApogeeEndListener(), new InterruptListener());
|
||||
log.debug("Maximum altitude was " + simulation.getSimulatedData().getBranch(0).getMaximum(FlightDataType.TYPE_ALTITUDE));
|
||||
return simulation.getSimulatedData().getBranch(0).getMaximum(FlightDataType.TYPE_ALTITUDE);
|
||||
} catch (MotorIgnitionException e) {
|
||||
@ -41,6 +43,8 @@ public class MaximumAltitudeParameter implements OptimizableParameter {
|
||||
} catch (SimulationLaunchException e) {
|
||||
// Other launch exceptions result in zero altitude
|
||||
return 0.0;
|
||||
} catch (SimulationCancelledException e) {
|
||||
throw (InterruptedException) new InterruptedException("Optimization was interrupted").initCause(e);
|
||||
} catch (SimulationException e) {
|
||||
// Other exceptions fail
|
||||
throw new OptimizationException(e);
|
||||
|
@ -7,9 +7,11 @@ import net.sf.openrocket.optimization.general.OptimizationException;
|
||||
import net.sf.openrocket.optimization.rocketoptimization.OptimizableParameter;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.exception.MotorIgnitionException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationCancelledException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationLaunchException;
|
||||
import net.sf.openrocket.simulation.listeners.system.ApogeeEndListener;
|
||||
import net.sf.openrocket.simulation.listeners.system.InterruptListener;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
|
||||
@ -29,10 +31,10 @@ public class MaximumVelocityParameter implements OptimizableParameter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double computeValue(Simulation simulation) throws OptimizationException {
|
||||
public double computeValue(Simulation simulation) throws OptimizationException, InterruptedException {
|
||||
try {
|
||||
log.debug("Running simulation to evaluate maximum velocity");
|
||||
simulation.simulate(new ApogeeEndListener());
|
||||
simulation.simulate(new ApogeeEndListener(), new InterruptListener());
|
||||
double value = simulation.getSimulatedData().getBranch(0).getMaximum(FlightDataType.TYPE_VELOCITY_TOTAL);
|
||||
log.debug("Maximum velocity was " + value);
|
||||
return value;
|
||||
@ -42,6 +44,8 @@ public class MaximumVelocityParameter implements OptimizableParameter {
|
||||
} catch (SimulationLaunchException e) {
|
||||
// Other launch exceptions result in zero velocity
|
||||
return Double.NaN;
|
||||
} catch (SimulationCancelledException e) {
|
||||
throw (InterruptedException) new InterruptedException("Optimization was interrupted").initCause(e);
|
||||
} catch (SimulationException e) {
|
||||
// Other exceptions fail
|
||||
throw new OptimizationException(e);
|
||||
|
@ -152,6 +152,18 @@ public class MathUtil {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Compute the minimum of three values. This is performed by direct comparison.
|
||||
* However, if one of the values is NaN and the other is not, the non-NaN value is
|
||||
* returned.
|
||||
*/
|
||||
public static double min(double w, double x, double y, double z) {
|
||||
return min(min(w, x), min(y, z));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute the maximum of three values. This is performed by direct comparison.
|
||||
* However, if one of the values is NaN and the other is not, the non-NaN value is
|
||||
|
@ -0,0 +1,34 @@
|
||||
package net.sf.openrocket.l10n;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.MissingResourceException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestResourceBundleTranslator {
|
||||
|
||||
@Test
|
||||
public void testSuccessfulUS() {
|
||||
ResourceBundleTranslator trans = new ResourceBundleTranslator("l10n.messages", Locale.US);
|
||||
assertEquals("messages.properties", trans.get("debug.currentFile"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccessfulFR() {
|
||||
ResourceBundleTranslator trans = new ResourceBundleTranslator("l10n.messages", Locale.FRENCH);
|
||||
assertEquals("messages_fr.properties", trans.get("debug.currentFile"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure() {
|
||||
ResourceBundleTranslator trans = new ResourceBundleTranslator("l10n.messages", Locale.US);
|
||||
try {
|
||||
fail("Returned: " + trans.get("missing"));
|
||||
} catch (MissingResourceException e) {
|
||||
// Expected
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -98,6 +98,16 @@ public class MathUtilTest {
|
||||
assertEquals(2.0, MathUtil.max(2.0, NaN, 1.0), 0);
|
||||
assertEquals(2.0, MathUtil.max(1.0, 2.0, NaN), 0);
|
||||
assertEquals(2.0, MathUtil.max(NaN, 2.0, 1.0), 0);
|
||||
|
||||
assertEquals(1.0, MathUtil.min(1.0, 2.0, 3.0, 4.0), 0);
|
||||
assertEquals(1.0, MathUtil.min(1.0, NaN, NaN, NaN), 0);
|
||||
assertEquals(1.0, MathUtil.min(NaN, 1.0, NaN, NaN), 0);
|
||||
assertEquals(1.0, MathUtil.min(NaN, NaN, 1.0, NaN), 0);
|
||||
assertEquals(1.0, MathUtil.min(2.0, NaN, 1.0, NaN), 0);
|
||||
assertEquals(1.0, MathUtil.min(2.0, NaN, NaN, 1.0), 0);
|
||||
assertEquals(1.0, MathUtil.min(1.0, 2.0, NaN, 3.0), 0);
|
||||
assertEquals(1.0, MathUtil.min(NaN, 2.0, 3.0, 1.0), 0);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Loading…
x
Reference in New Issue
Block a user