1 package net.sf.openrocket.optimization.rocketoptimization;
3 import java.util.Arrays;
5 import java.util.concurrent.ConcurrentHashMap;
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;
16 * A Function that optimizes a specific RocketOptimizationParameter to some goal
17 * by modifying a base simulation using SimulationModifiers.
19 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
21 public class RocketOptimizationFunction implements Function {
22 private static final LogHelper log = Application.getLogger();
24 private static final double OUTSIDE_DOMAIN_SCALE = 1.0e200;
27 * NOTE: This class must be thread-safe!!!
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;
36 private final Map<Point, Double> parameterValueCache = new ConcurrentHashMap<Point, Double>();
37 private final Map<Point, Double> goalValueCache = new ConcurrentHashMap<Point, Double>();
43 * The dimensionality of the resulting function is the same as the length of the
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
51 public RocketOptimizationFunction(Simulation baseSimulation, OptimizableParameter parameter,
52 OptimizationGoal goal, SimulationDomain domain, SimulationModifier... modifiers) {
53 this.baseSimulation = baseSimulation;
54 this.parameter = parameter;
57 this.modifiers = modifiers.clone();
58 if (modifiers.length == 0) {
59 throw new IllegalArgumentException("No SimulationModifiers specified");
65 public double evaluate(Point point) throws InterruptedException, OptimizationException {
67 * parameterValue is the computed parameter value (e.g. altitude)
68 * goalValue is the value that needs to be minimized
70 double goalValue, parameterValue;
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);
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 double distance = domain.getDistanceToDomain(simulation);
96 if (distance > 0 || Double.isNaN(distance)) {
97 if (Double.isNaN(distance)) {
98 goalValue = Double.MAX_VALUE;
100 goalValue = (distance + 1) * OUTSIDE_DOMAIN_SCALE;
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);
109 // Compute the optimization value
110 parameterValue = parameter.computeValue(simulation);
111 parameterValueCache.put(point, parameterValue);
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;
120 goalValueCache.put(point, goalValue);
122 log.verbose("Parameter value at point " + point + " is " + goalValue + ", goal function value=" + goalValue);
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.
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.
137 public double getComputedParameterValue(Point point) {
138 Double value = parameterValueCache.get(point);
148 * Returns a new deep copy of the simulation and rocket. This methods performs
149 * synchronization on the simulation for thread protection.
151 * Note: This method is package-private for unit testing purposes.
153 * @return a new deep copy of the simulation and rocket
155 Simulation newSimulationInstance(Simulation simulation) {
156 synchronized (baseSimulation) {
157 Rocket newRocket = (Rocket) simulation.getRocket().copy();
158 Simulation newSimulation = simulation.duplicateSimulation(newRocket);
159 return newSimulation;