create changelog entry
[debian/openrocket] / core / 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                 /*
71                  * parameterValue is the computed parameter value (e.g. altitude)
72                  * goalValue is the value that needs to be minimized
73                  */
74                 double goalValue, parameterValue;
75                 
76                 log.debug("Computing optimization function value at point " + point);
77                 
78                 // Create the new simulation based on the point
79                 double[] p = point.asArray();
80                 if (p.length != modifiers.length) {
81                         throw new IllegalArgumentException("Point has length " + p.length + " while function has " +
82                                         modifiers.length + " simulation modifiers");
83                 }
84                 
85                 Simulation simulation = newSimulationInstance(baseSimulation);
86                 for (int i = 0; i < modifiers.length; i++) {
87                         modifiers[i].modify(simulation, p[i]);
88                 }
89                 
90
91                 // Check whether the point is within the simulation domain
92                 Pair<Double, Value> d = domain.getDistanceToDomain(simulation);
93                 double distance = d.getU();
94                 Value referenceValue = d.getV();
95                 if (distance > 0 || Double.isNaN(distance)) {
96                         if (Double.isNaN(distance)) {
97                                 goalValue = Double.MAX_VALUE;
98                         } else {
99                                 goalValue = (distance + 1) * OUTSIDE_DOMAIN_SCALE;
100                         }
101                         log.debug("Optimization point is outside of domain, distance=" + distance + " goal function value=" + goalValue);
102                         
103                         fireEvent(simulation, point, referenceValue, null, goalValue);
104                         
105                         return goalValue;
106                 }
107                 
108
109                 // Compute the optimization value
110                 parameterValue = parameter.computeValue(simulation);
111                 goalValue = goal.getMinimizationParameter(parameterValue);
112                 
113                 if (Double.isNaN(goalValue)) {
114                         log.warn("Computed goal value was NaN, baseSimulation=" + baseSimulation + " parameter=" + parameter +
115                                         " goal=" + goal + " modifiers=" + Arrays.toString(modifiers) + " simulation=" + simulation +
116                                         " parameter value=" + parameterValue);
117                         goalValue = Double.MAX_VALUE;
118                 }
119                 
120                 log.verbose("Parameter value at point " + point + " is " + parameterValue + ", goal function value=" + goalValue);
121                 
122                 fireEvent(simulation, point, referenceValue, new Value(parameterValue, parameter.getUnitGroup().getDefaultUnit()),
123                                 goalValue);
124                 
125                 return goalValue;
126         }
127         
128         
129
130
131
132         /**
133          * Returns a new deep copy of the simulation and rocket.  This methods performs
134          * synchronization on the simulation for thread protection.
135          * <p>
136          * Note:  This method is package-private for unit testing purposes.
137          * 
138          * @return      a new deep copy of the simulation and rocket
139          */
140         Simulation newSimulationInstance(Simulation simulation) {
141                 synchronized (baseSimulation) {
142                         Rocket newRocket = simulation.getRocket().copyWithOriginalID();
143                         Simulation newSimulation = simulation.duplicateSimulation(newRocket);
144                         return newSimulation;
145                 }
146         }
147         
148         
149         /**
150          * Add a listener to this function.  The listener will be notified each time the
151          * function is successfully evaluated.
152          * <p>
153          * Note that the listener may be called from other threads and must be thread-safe!
154          * 
155          * @param listener      the listener to add.
156          */
157         public void addRocketOptimizationListener(RocketOptimizationListener listener) {
158                 listeners.add(listener);
159         }
160         
161         public void removeRocketOptimizationListener(RocketOptimizationListener listener) {
162                 listeners.remove(listener);
163         }
164         
165         
166
167         private void fireEvent(Simulation simulation, Point p, Value domainReference, Value parameterValue, double goalValue)
168                         throws OptimizationException {
169                 
170                 if (listeners.isEmpty()) {
171                         return;
172                 }
173                 
174
175                 Value[] values = new Value[p.dim()];
176                 for (int i = 0; i < values.length; i++) {
177                         double value = modifiers[i].getCurrentSIValue(simulation);
178                         UnitGroup unit = modifiers[i].getUnitGroup();
179                         values[i] = new Value(value, unit.getDefaultUnit());
180                 }
181                 
182                 for (RocketOptimizationListener l : listeners) {
183                         l.evaluated(p, values, domainReference, parameterValue, goalValue);
184                 }
185         }
186 }