bug fixes and rocket optimization
[debian/openrocket] / src / net / sf / openrocket / optimization / rocketoptimization / RocketOptimizationFunction.java
1 package net.sf.openrocket.optimization.rocketoptimization;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.List;
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 import net.sf.openrocket.unit.UnitGroup;
15 import net.sf.openrocket.unit.Value;
16 import net.sf.openrocket.util.Pair;
17
18 /**
19  * A Function that optimizes a specific RocketOptimizationParameter to some goal
20  * by modifying a base simulation using SimulationModifiers.
21  * 
22  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
23  */
24 public class RocketOptimizationFunction implements Function {
25         private static final LogHelper log = Application.getLogger();
26         
27         private static final double OUTSIDE_DOMAIN_SCALE = 1.0e200;
28         
29         /*
30          * NOTE:  This class must be thread-safe!!!
31          */
32
33         private final Simulation baseSimulation;
34         private final OptimizableParameter parameter;
35         private final OptimizationGoal goal;
36         private final SimulationDomain domain;
37         private final SimulationModifier[] modifiers;
38         
39
40         private final List<RocketOptimizationListener> listeners = new ArrayList<RocketOptimizationListener>();
41         
42         
43         /**
44          * Sole constructor.
45          * <p>
46          * The dimensionality of the resulting function is the same as the length of the
47          * modifiers array.
48          * 
49          * @param baseSimulation        the base simulation to modify
50          * @param parameter                     the rocket parameter to optimize
51          * @param goal                          the goal of the rocket parameter
52          * @param modifiers                     the modifiers that modify the simulation
53          */
54         public RocketOptimizationFunction(Simulation baseSimulation, OptimizableParameter parameter,
55                         OptimizationGoal goal, SimulationDomain domain, SimulationModifier... modifiers) {
56                 this.baseSimulation = baseSimulation;
57                 this.parameter = parameter;
58                 this.goal = goal;
59                 this.domain = domain;
60                 this.modifiers = modifiers.clone();
61                 if (modifiers.length == 0) {
62                         throw new IllegalArgumentException("No SimulationModifiers specified");
63                 }
64         }
65         
66         
67         @Override
68         public double evaluate(Point point) throws InterruptedException, OptimizationException {
69                 
70                 System.out.println("Evaluating function at point " + point);
71                 
72                 /*
73                  * parameterValue is the computed parameter value (e.g. altitude)
74                  * goalValue is the value that needs to be minimized
75                  */
76                 double goalValue, parameterValue;
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                 Pair<Double, Double> d = domain.getDistanceToDomain(simulation);
96                 double distance = d.getU();
97                 double referenceValue = d.getV();
98                 if (distance > 0 || Double.isNaN(distance)) {
99                         if (Double.isNaN(distance)) {
100                                 goalValue = Double.MAX_VALUE;
101                         } else {
102                                 goalValue = (distance + 1) * OUTSIDE_DOMAIN_SCALE;
103                         }
104                         log.verbose("Optimization point is outside of domain, distance=" + distance + " goal function value=" + goalValue);
105                         System.out.println("Optimization point is outside of domain, distance=" + distance + " goal function value=" + goalValue);
106                         
107                         fireEvent(simulation, point, referenceValue, Double.NaN, goalValue);
108                         
109                         return goalValue;
110                 }
111                 
112
113                 // Compute the optimization value
114                 parameterValue = parameter.computeValue(simulation);
115                 goalValue = goal.getMinimizationParameter(parameterValue);
116                 
117                 if (Double.isNaN(goalValue)) {
118                         log.warn("Computed goal value was NaN, baseSimulation=" + baseSimulation + " parameter=" + parameter +
119                                         " goal=" + goal + " modifiers=" + Arrays.toString(modifiers) + " simulation=" + simulation +
120                                         " parameter value=" + parameterValue);
121                         goalValue = Double.MAX_VALUE;
122                 }
123                 
124                 log.verbose("Parameter value at point " + point + " is " + parameterValue + ", goal function value=" + goalValue);
125                 System.out.println("Parameter value at point " + point + " is " + parameterValue + ", goal function value=" + goalValue);
126                 
127                 fireEvent(simulation, point, referenceValue, parameterValue, goalValue);
128                 
129                 return goalValue;
130         }
131         
132         
133
134
135
136         /**
137          * Returns a new deep copy of the simulation and rocket.  This methods performs
138          * synchronization on the simulation for thread protection.
139          * <p>
140          * Note:  This method is package-private for unit testing purposes.
141          * 
142          * @return      a new deep copy of the simulation and rocket
143          */
144         Simulation newSimulationInstance(Simulation simulation) {
145                 synchronized (baseSimulation) {
146                         Rocket newRocket = simulation.getRocket().copyWithOriginalID();
147                         Simulation newSimulation = simulation.duplicateSimulation(newRocket);
148                         return newSimulation;
149                 }
150         }
151         
152         
153         /**
154          * Add a listener to this function.  The listener will be notified each time the
155          * function is successfully evaluated.
156          * <p>
157          * Note that the listener may be called from other threads and must be thread-safe!
158          * 
159          * @param listener      the listener to add.
160          */
161         public void addRocketOptimizationListener(RocketOptimizationListener listener) {
162                 listeners.add(listener);
163         }
164         
165         public void removeRocketOptimizationListener(RocketOptimizationListener listener) {
166                 listeners.remove(listener);
167         }
168         
169         
170
171         private void fireEvent(Simulation simulation, Point p, double domainReference, double parameterValue, double goalValue)
172                         throws OptimizationException {
173                 
174                 if (listeners.isEmpty()) {
175                         return;
176                 }
177                 
178
179                 Value[] values = new Value[p.dim()];
180                 for (int i = 0; i < values.length; i++) {
181                         double value = modifiers[i].getCurrentSIValue(simulation);
182                         UnitGroup unit = modifiers[i].getUnitGroup();
183                         values[i] = new Value(value, unit.getDefaultUnit());
184                 }
185                 
186                 for (RocketOptimizationListener l : listeners) {
187                         l.evaluated(p, values, domainReference, parameterValue, goalValue);
188                 }
189         }
190 }