package net.sf.openrocket.optimization.rocketoptimization;
+import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.List;
import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.optimization.general.Function;
+import net.sf.openrocket.optimization.general.OptimizationException;
import net.sf.openrocket.optimization.general.Point;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.startup.Application;
+import net.sf.openrocket.unit.UnitGroup;
+import net.sf.openrocket.unit.Value;
+import net.sf.openrocket.util.Pair;
/**
* A Function that optimizes a specific RocketOptimizationParameter to some goal
public class RocketOptimizationFunction implements Function {
private static final LogHelper log = Application.getLogger();
+ private static final double OUTSIDE_DOMAIN_SCALE = 1.0e200;
+
/*
* NOTE: This class must be thread-safe!!!
*/
private final Simulation baseSimulation;
- private final RocketOptimizationParameter parameter;
+ private final OptimizableParameter parameter;
private final OptimizationGoal goal;
+ private final SimulationDomain domain;
private final SimulationModifier[] modifiers;
- private final Map<Point, Double> parameterValueCache = new ConcurrentHashMap<Point, Double>();
- private final Map<Point, Double> goalValueCache = new ConcurrentHashMap<Point, Double>();
+
+ private final List<RocketOptimizationListener> listeners = new ArrayList<RocketOptimizationListener>();
/**
* @param goal the goal of the rocket parameter
* @param modifiers the modifiers that modify the simulation
*/
- public RocketOptimizationFunction(Simulation baseSimulation, RocketOptimizationParameter parameter,
- OptimizationGoal goal, SimulationModifier... modifiers) {
+ public RocketOptimizationFunction(Simulation baseSimulation, OptimizableParameter parameter,
+ OptimizationGoal goal, SimulationDomain domain, SimulationModifier... modifiers) {
this.baseSimulation = baseSimulation;
this.parameter = parameter;
this.goal = goal;
+ this.domain = domain;
this.modifiers = modifiers.clone();
if (modifiers.length == 0) {
throw new IllegalArgumentException("No SimulationModifiers specified");
@Override
- public double evaluate(Point point) throws InterruptedException {
+ public double evaluate(Point point) throws InterruptedException, OptimizationException {
- // Check for precomputed value
- double value = preComputed(point);
- if (!Double.isNaN(value)) {
- return value;
- }
+ System.out.println("Evaluating function at point " + point);
+
+ /*
+ * parameterValue is the computed parameter value (e.g. altitude)
+ * goalValue is the value that needs to be minimized
+ */
+ double goalValue, parameterValue;
+
+
+ log.verbose("Computing optimization function value at point " + point);
// Create the new simulation based on the point
double[] p = point.asArray();
throw new IllegalArgumentException("Point has length " + p.length + " while function has " +
modifiers.length + " simulation modifiers");
}
- Simulation simulation = newSimulationInstance();
+
+ Simulation simulation = newSimulationInstance(baseSimulation);
for (int i = 0; i < modifiers.length; i++) {
modifiers[i].modify(simulation, p[i]);
}
+
+ // Check whether the point is within the simulation domain
+ Pair<Double, Double> d = domain.getDistanceToDomain(simulation);
+ double distance = d.getU();
+ double referenceValue = d.getV();
+ if (distance > 0 || Double.isNaN(distance)) {
+ if (Double.isNaN(distance)) {
+ goalValue = Double.MAX_VALUE;
+ } else {
+ goalValue = (distance + 1) * OUTSIDE_DOMAIN_SCALE;
+ }
+ log.verbose("Optimization point is outside of domain, distance=" + distance + " goal function value=" + goalValue);
+ System.out.println("Optimization point is outside of domain, distance=" + distance + " goal function value=" + goalValue);
+
+ fireEvent(simulation, point, referenceValue, Double.NaN, goalValue);
+
+ return goalValue;
+ }
+
+
// Compute the optimization value
- value = parameter.computeValue(simulation);
- parameterValueCache.put(point, value);
+ parameterValue = parameter.computeValue(simulation);
+ goalValue = goal.getMinimizationParameter(parameterValue);
- value = goal.getMinimizationParameter(value);
- if (Double.isNaN(value)) {
- log.warn("Computed value was NaN, baseSimulation=" + baseSimulation + " parameter=" + parameter +
- " goal=" + goal + " modifiers=" + Arrays.toString(modifiers) + " simulation=" + simulation);
- value = Double.MAX_VALUE;
+ if (Double.isNaN(goalValue)) {
+ log.warn("Computed goal value was NaN, baseSimulation=" + baseSimulation + " parameter=" + parameter +
+ " goal=" + goal + " modifiers=" + Arrays.toString(modifiers) + " simulation=" + simulation +
+ " parameter value=" + parameterValue);
+ goalValue = Double.MAX_VALUE;
}
- goalValueCache.put(point, value);
- return value;
- }
-
- @Override
- public double preComputed(Point point) {
- Double value = goalValueCache.get(point);
- if (value != null) {
- return value;
- }
+ log.verbose("Parameter value at point " + point + " is " + parameterValue + ", goal function value=" + goalValue);
+ System.out.println("Parameter value at point " + point + " is " + parameterValue + ", goal function value=" + goalValue);
+
+ fireEvent(simulation, point, referenceValue, parameterValue, goalValue);
- // TODO: : is in domain?
- return 0;
+ return goalValue;
}
+
+
+
/**
- * Return the parameter value at a point that has been computed. The purpose is
- * to allow retrieving the parameter value corresponding to the found minimum value.
+ * Returns a new deep copy of the simulation and rocket. This methods performs
+ * synchronization on the simulation for thread protection.
+ * <p>
+ * Note: This method is package-private for unit testing purposes.
*
- * @param point the point to use.
- * @return the parameter value at that point, or NaN if the value at this point has not been computed.
+ * @return a new deep copy of the simulation and rocket
*/
- public double getComputedParameterValue(Point point) {
- Double value = parameterValueCache.get(point);
- if (value != null) {
- return value;
- } else {
- return Double.NaN;
+ Simulation newSimulationInstance(Simulation simulation) {
+ synchronized (baseSimulation) {
+ Rocket newRocket = simulation.getRocket().copyWithOriginalID();
+ Simulation newSimulation = simulation.duplicateSimulation(newRocket);
+ return newSimulation;
}
}
/**
- * Returns a new deep copy of the simulation and rocket. This methods performs
- * synchronization on the simulation for thread protection.
+ * Add a listener to this function. The listener will be notified each time the
+ * function is successfully evaluated.
+ * <p>
+ * Note that the listener may be called from other threads and must be thread-safe!
*
- * @return
+ * @param listener the listener to add.
*/
- private Simulation newSimulationInstance() {
- synchronized (baseSimulation) {
- Rocket newRocket = (Rocket) baseSimulation.getRocket().copy();
- Simulation newSimulation = baseSimulation.duplicateSimulation(newRocket);
- return newSimulation;
- }
+ public void addRocketOptimizationListener(RocketOptimizationListener listener) {
+ listeners.add(listener);
+ }
+
+ public void removeRocketOptimizationListener(RocketOptimizationListener listener) {
+ listeners.remove(listener);
}
+
+
+ private void fireEvent(Simulation simulation, Point p, double domainReference, double parameterValue, double goalValue)
+ throws OptimizationException {
+
+ if (listeners.isEmpty()) {
+ return;
+ }
+
+
+ Value[] values = new Value[p.dim()];
+ for (int i = 0; i < values.length; i++) {
+ double value = modifiers[i].getCurrentSIValue(simulation);
+ UnitGroup unit = modifiers[i].getUnitGroup();
+ values[i] = new Value(value, unit.getDefaultUnit());
+ }
+
+ for (RocketOptimizationListener l : listeners) {
+ l.evaluated(p, values, domainReference, parameterValue, goalValue);
+ }
+ }
}