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 {
71 * parameterValue is the computed parameter value (e.g. altitude)
72 * goalValue is the value that needs to be minimized
74 double goalValue, parameterValue;
76 log.debug("Computing optimization function value at point " + point);
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");
85 Simulation simulation = newSimulationInstance(baseSimulation);
86 for (int i = 0; i < modifiers.length; i++) {
87 modifiers[i].modify(simulation, p[i]);
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;
99 goalValue = (distance + 1) * OUTSIDE_DOMAIN_SCALE;
101 log.debug("Optimization point is outside of domain, distance=" + distance + " goal function value=" + goalValue);
103 fireEvent(simulation, point, referenceValue, null, goalValue);
109 // Compute the optimization value
110 parameterValue = parameter.computeValue(simulation);
111 goalValue = goal.getMinimizationParameter(parameterValue);
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;
120 log.verbose("Parameter value at point " + point + " is " + parameterValue + ", goal function value=" + goalValue);
122 fireEvent(simulation, point, referenceValue, new Value(parameterValue, parameter.getUnitGroup().getDefaultUnit()),
133 * Returns a new deep copy of the simulation and rocket. This methods performs
134 * synchronization on the simulation for thread protection.
136 * Note: This method is package-private for unit testing purposes.
138 * @return a new deep copy of the simulation and rocket
140 Simulation newSimulationInstance(Simulation simulation) {
141 synchronized (baseSimulation) {
142 Rocket newRocket = simulation.getRocket().copyWithOriginalID();
143 Simulation newSimulation = simulation.duplicateSimulation(newRocket);
144 return newSimulation;
150 * Add a listener to this function. The listener will be notified each time the
151 * function is successfully evaluated.
153 * Note that the listener may be called from other threads and must be thread-safe!
155 * @param listener the listener to add.
157 public void addRocketOptimizationListener(RocketOptimizationListener listener) {
158 listeners.add(listener);
161 public void removeRocketOptimizationListener(RocketOptimizationListener listener) {
162 listeners.remove(listener);
167 private void fireEvent(Simulation simulation, Point p, Value domainReference, Value parameterValue, double goalValue)
168 throws OptimizationException {
170 if (listeners.isEmpty()) {
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());
182 for (RocketOptimizationListener l : listeners) {
183 l.evaluated(p, values, domainReference, parameterValue, goalValue);