DGP - merged printing support from branch
[debian/openrocket] / src / net / sf / openrocket / optimization / rocketoptimization / RocketOptimizationFunction.java
1 package net.sf.openrocket.optimization.rocketoptimization;
2
3 import java.util.Arrays;
4 import java.util.Map;
5 import java.util.concurrent.ConcurrentHashMap;
6
7 import net.sf.openrocket.document.Simulation;
8 import net.sf.openrocket.logging.LogHelper;
9 import net.sf.openrocket.optimization.general.Function;
10 import net.sf.openrocket.optimization.general.OptimizationException;
11 import net.sf.openrocket.optimization.general.Point;
12 import net.sf.openrocket.rocketcomponent.Rocket;
13 import net.sf.openrocket.startup.Application;
14
15 /**
16  * A Function that optimizes a specific RocketOptimizationParameter to some goal
17  * by modifying a base simulation using SimulationModifiers.
18  * 
19  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
20  */
21 public class RocketOptimizationFunction implements Function {
22         private static final LogHelper log = Application.getLogger();
23         
24         private static final double OUTSIDE_DOMAIN_SCALE = 1.0e200;
25         
26         /*
27          * NOTE:  This class must be thread-safe!!!
28          */
29
30         private final Simulation baseSimulation;
31         private final OptimizableParameter parameter;
32         private final OptimizationGoal goal;
33         private final SimulationDomain domain;
34         private final SimulationModifier[] modifiers;
35         
36         private final Map<Point, Double> parameterValueCache = new ConcurrentHashMap<Point, Double>();
37         private final Map<Point, Double> goalValueCache = new ConcurrentHashMap<Point, Double>();
38         
39         
40         /**
41          * Sole constructor.
42          * <p>
43          * The dimensionality of the resulting function is the same as the length of the
44          * modifiers array.
45          * 
46          * @param baseSimulation        the base simulation to modify
47          * @param parameter                     the rocket parameter to optimize
48          * @param goal                          the goal of the rocket parameter
49          * @param modifiers                     the modifiers that modify the simulation
50          */
51         public RocketOptimizationFunction(Simulation baseSimulation, OptimizableParameter parameter,
52                         OptimizationGoal goal, SimulationDomain domain, SimulationModifier... modifiers) {
53                 this.baseSimulation = baseSimulation;
54                 this.parameter = parameter;
55                 this.goal = goal;
56                 this.domain = domain;
57                 this.modifiers = modifiers.clone();
58                 if (modifiers.length == 0) {
59                         throw new IllegalArgumentException("No SimulationModifiers specified");
60                 }
61         }
62         
63         
64         @Override
65         public double evaluate(Point point) throws InterruptedException, OptimizationException {
66                 /*
67                  * parameterValue is the computed parameter value (e.g. altitude)
68                  * goalValue is the value that needs to be minimized
69                  */
70                 double goalValue, parameterValue;
71                 
72                 // Check for precomputed value
73                 Double d = goalValueCache.get(point);
74                 if (d != null && !Double.isNaN(d)) {
75                         log.verbose("Optimization function value at point " + point + " was found in cache: " + d);
76                         return d;
77                 }
78                 
79                 log.verbose("Computing optimization function value at point " + point);
80                 
81                 // Create the new simulation based on the point
82                 double[] p = point.asArray();
83                 if (p.length != modifiers.length) {
84                         throw new IllegalArgumentException("Point has length " + p.length + " while function has " +
85                                         modifiers.length + " simulation modifiers");
86                 }
87                 
88                 Simulation simulation = newSimulationInstance(baseSimulation);
89                 for (int i = 0; i < modifiers.length; i++) {
90                         modifiers[i].modify(simulation, p[i]);
91                 }
92                 
93
94                 // Check whether the point is within the simulation domain
95                 double distance = domain.getDistanceToDomain(simulation);
96                 if (distance > 0 || Double.isNaN(distance)) {
97                         if (Double.isNaN(distance)) {
98                                 goalValue = Double.MAX_VALUE;
99                         } else {
100                                 goalValue = (distance + 1) * OUTSIDE_DOMAIN_SCALE;
101                         }
102                         parameterValueCache.put(point, Double.NaN);
103                         goalValueCache.put(point, goalValue);
104                         log.verbose("Optimization point is outside of domain, distance=" + distance + " goal function value=" + goalValue);
105                         return goalValue;
106                 }
107                 
108
109                 // Compute the optimization value
110                 parameterValue = parameter.computeValue(simulation);
111                 parameterValueCache.put(point, parameterValue);
112                 
113                 goalValue = goal.getMinimizationParameter(parameterValue);
114                 if (Double.isNaN(goalValue)) {
115                         log.warn("Computed goal value was NaN, baseSimulation=" + baseSimulation + " parameter=" + parameter +
116                                         " goal=" + goal + " modifiers=" + Arrays.toString(modifiers) + " simulation=" + simulation +
117                                         " parameter value=" + parameterValue);
118                         goalValue = Double.MAX_VALUE;
119                 }
120                 goalValueCache.put(point, goalValue);
121                 
122                 log.verbose("Parameter value at point " + point + " is " + goalValue + ", goal function value=" + goalValue);
123                 
124                 return goalValue;
125         }
126         
127         
128
129
130         /**
131          * Return the parameter value at a point that has been computed.  The purpose is
132          * to allow retrieving the parameter value corresponding to the found minimum value.
133          * 
134          * @param point         the point to use.
135          * @return                      the parameter value at that point, or NaN if the value at this point has not been computed.
136          */
137         public double getComputedParameterValue(Point point) {
138                 Double value = parameterValueCache.get(point);
139                 if (value != null) {
140                         return value;
141                 } else {
142                         return Double.NaN;
143                 }
144         }
145         
146         
147         /**
148          * Returns a new deep copy of the simulation and rocket.  This methods performs
149          * synchronization on the simulation for thread protection.
150          * <p>
151          * Note:  This method is package-private for unit testing purposes.
152          * 
153          * @return      a new deep copy of the simulation and rocket
154          */
155         Simulation newSimulationInstance(Simulation simulation) {
156                 synchronized (baseSimulation) {
157                         Rocket newRocket = (Rocket) simulation.getRocket().copy();
158                         Simulation newSimulation = simulation.duplicateSimulation(newRocket);
159                         return newSimulation;
160                 }
161         }
162         
163 }