bug fixes and rocket optimization
[debian/openrocket] / src / net / sf / openrocket / optimization / rocketoptimization / RocketOptimizationFunction.java
index c975a8a4fc41bc71b6f4f2c57376883ddcb2a5ba..b0e8e8430b5094a6457c0ce2ef03ec057ed38a8e 100644 (file)
@@ -1,15 +1,19 @@
 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
@@ -20,17 +24,20 @@ import net.sf.openrocket.startup.Application;
 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>();
        
        
        /**
@@ -44,11 +51,12 @@ public class RocketOptimizationFunction implements Function {
         * @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");
@@ -57,13 +65,18 @@ public class RocketOptimizationFunction implements Function {
        
        
        @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();
@@ -71,67 +84,107 @@ public class RocketOptimizationFunction implements Function {
                        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);
+               }
+       }
 }