1 package net.sf.openrocket.optimization.rocketoptimization;
3 import java.util.ArrayList;
4 import java.util.Arrays;
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;
19 * A Function that optimizes a specific RocketOptimizationParameter to some goal
20 * by modifying a base simulation using SimulationModifiers.
22 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
24 public class RocketOptimizationFunction implements Function {
25 private static final LogHelper log = Application.getLogger();
27 private static final double OUTSIDE_DOMAIN_SCALE = 1.0e200;
30 * NOTE: This class must be thread-safe!!!
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;
40 private final List<RocketOptimizationListener> listeners = new ArrayList<RocketOptimizationListener>();
46 * The dimensionality of the resulting function is the same as the length of the
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
54 public RocketOptimizationFunction(Simulation baseSimulation, OptimizableParameter parameter,
55 OptimizationGoal goal, SimulationDomain domain, SimulationModifier... modifiers) {
56 this.baseSimulation = baseSimulation;
57 this.parameter = parameter;
60 this.modifiers = modifiers.clone();
61 if (modifiers.length == 0) {
62 throw new IllegalArgumentException("No SimulationModifiers specified");
68 public double evaluate(Point point) throws InterruptedException, OptimizationException {
70 System.out.println("Evaluating function at point " + point);
73 * parameterValue is the computed parameter value (e.g. altitude)
74 * goalValue is the value that needs to be minimized
76 double goalValue, parameterValue;
79 log.verbose("Computing optimization function value at point " + point);
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");
88 Simulation simulation = newSimulationInstance(baseSimulation);
89 for (int i = 0; i < modifiers.length; i++) {
90 modifiers[i].modify(simulation, p[i]);
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;
102 goalValue = (distance + 1) * OUTSIDE_DOMAIN_SCALE;
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);
107 fireEvent(simulation, point, referenceValue, Double.NaN, goalValue);
113 // Compute the optimization value
114 parameterValue = parameter.computeValue(simulation);
115 goalValue = goal.getMinimizationParameter(parameterValue);
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;
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);
127 fireEvent(simulation, point, referenceValue, parameterValue, goalValue);
137 * Returns a new deep copy of the simulation and rocket. This methods performs
138 * synchronization on the simulation for thread protection.
140 * Note: This method is package-private for unit testing purposes.
142 * @return a new deep copy of the simulation and rocket
144 Simulation newSimulationInstance(Simulation simulation) {
145 synchronized (baseSimulation) {
146 Rocket newRocket = simulation.getRocket().copyWithOriginalID();
147 Simulation newSimulation = simulation.duplicateSimulation(newRocket);
148 return newSimulation;
154 * Add a listener to this function. The listener will be notified each time the
155 * function is successfully evaluated.
157 * Note that the listener may be called from other threads and must be thread-safe!
159 * @param listener the listener to add.
161 public void addRocketOptimizationListener(RocketOptimizationListener listener) {
162 listeners.add(listener);
165 public void removeRocketOptimizationListener(RocketOptimizationListener listener) {
166 listeners.remove(listener);
171 private void fireEvent(Simulation simulation, Point p, double domainReference, double parameterValue, double goalValue)
172 throws OptimizationException {
174 if (listeners.isEmpty()) {
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());
186 for (RocketOptimizationListener l : listeners) {
187 l.evaluated(p, values, domainReference, parameterValue, goalValue);