1 | package dk.deepthought.sidious.rules; |
2 | |
3 | import java.util.ArrayList; |
4 | import java.util.Arrays; |
5 | import java.util.Collection; |
6 | import java.util.List; |
7 | |
8 | import org.apache.commons.logging.Log; |
9 | import org.apache.commons.logging.LogFactory; |
10 | |
11 | import dk.deepthought.sidious.goalhandler.Goal; |
12 | import dk.deepthought.sidious.greenhouse.ClimaticState; |
13 | import dk.deepthought.sidious.greenhouse.LeafPhotosynthesisModel; |
14 | import dk.deepthought.sidious.greenhouse.ScreenSetPoint; |
15 | import dk.deepthought.sidious.greenhouse.SensorInput; |
16 | import dk.deepthought.sidious.services.ServiceEngine; |
17 | import dk.deepthought.sidious.supportsystem.Adjustable; |
18 | import dk.deepthought.sidious.supportsystem.State; |
19 | import dk.deepthought.sidious.supportsystem.Step; |
20 | import dk.deepthought.sidious.supportsystem.SuperLinkID; |
21 | import dk.deepthought.sidious.supportsystem.SystemSettings; |
22 | import dk.deepthought.sidious.util.RuleProperty; |
23 | |
24 | /** |
25 | * Class represents a photosynthesis rule. |
26 | * <p> |
27 | * It is responsible for maintaining a desired photosynthesis rate. |
28 | * |
29 | * @author Deepthought |
30 | * |
31 | */ |
32 | public class PhotosynthesisRule extends Rule { |
33 | /** |
34 | * Logger for this class |
35 | */ |
36 | private static final Log logger = LogFactory |
37 | .getLog(PhotosynthesisRule.class); |
38 | |
39 | /** |
40 | * The RuleProperty of this class |
41 | */ |
42 | private static RuleProperty ruleProperty; |
43 | |
44 | /** |
45 | * Value is defaulted in the calculate method. |
46 | */ |
47 | private static final double GLASS_FACTOR = Double.NaN; |
48 | |
49 | /** |
50 | * Value is defaulted in the calculate method. |
51 | */ |
52 | private static final double SHADE_FACTOR = Double.NaN; |
53 | |
54 | // TODO extract into rule property file. |
55 | static final int T_START = 18; |
56 | |
57 | // TODO extract into rule property file. |
58 | static final int CO2_START = 300; |
59 | |
60 | // TODO extract into rule property file. |
61 | static final int CO2_INCREMENT = 100; |
62 | |
63 | // TODO extract into rule property file. |
64 | private static final int T_END = 36; |
65 | |
66 | // TODO extract into rule property file. |
67 | private static final int CO2_END = 1600; |
68 | |
69 | // TODO extract into rule property file. |
70 | private static final double DESIRED_RATE = 0.8; |
71 | |
72 | /** |
73 | * Value sets the importance of this rule. |
74 | * TODO extract into rule property file |
75 | */ |
76 | private double tweakValue = 1; |
77 | |
78 | /** |
79 | * Used to keep track of the amount of model calculations done by this |
80 | */ |
81 | private static long calculationCounter = 0; |
82 | |
83 | |
84 | public PhotosynthesisRule(final SuperLinkID parentID) { |
85 | if (ruleProperty == null) { |
86 | ruleProperty = new RuleProperty(this.getClass().getSimpleName()); |
87 | } |
88 | setParentID(parentID); |
89 | } |
90 | |
91 | /* |
92 | * (non-Javadoc) |
93 | * |
94 | * @see dk.deepthought.sidious.rules.Rule#desire(dk.deepthought.sidious.supportsystem.State, |
95 | * dk.deepthought.sidious.supportsystem.State, dk.deepthought.sidious.supportsystem.Step) |
96 | */ |
97 | public double desire(State currentState, State newState, Step step) { |
98 | if (logger.isDebugEnabled()) { |
99 | logger.debug("desire(State currentState=" + currentState |
100 | + ", State newState=" + newState + ", Step step=" + step |
101 | + ") - start"); |
102 | } |
103 | double desire = 0; |
104 | double oldRate = calculateRate(currentState); |
105 | double newRate = calculateRate(newState); |
106 | double deltaRate = oldRate - newRate; |
107 | if (deltaRate > 0) { |
108 | desire += deltaRate; |
109 | } |
110 | desire += (DESIRED_RATE - newRate); |
111 | |
112 | if (newRate > DESIRED_RATE) { |
113 | desire = Math.abs(desire); |
114 | } |
115 | if (Math.abs(newRate - DESIRED_RATE) < 0.001) { |
116 | desire = 0; |
117 | } |
118 | if (desire > 1) { |
119 | desire = 1; |
120 | } |
121 | if (logger.isDebugEnabled()) { |
122 | logger.debug("desire(State currentState=" + currentState |
123 | + ", State newState=" + newState + ", Step step=" + step |
124 | + ") - end - return value=" + desire); |
125 | } |
126 | return desire; |
127 | } |
128 | |
129 | /** |
130 | * Returns the desire related to <code>DESIRED_RATE</code>. |
131 | * |
132 | * @param state |
133 | * the state containing needed sensors |
134 | * @param step |
135 | * the step containing needed adjustables |
136 | * @return the calculated desire |
137 | */ |
138 | double calculateDesire(State state, Step step) { |
139 | if (logger.isDebugEnabled()) { |
140 | logger.debug("calculateDesire(State state=" + state |
141 | + ", Step step=" + step + ") - start"); |
142 | } |
143 | double rate = calculateRate(state); |
144 | |
145 | double returndouble = Math.abs(rate - DESIRED_RATE) / DESIRED_RATE; |
146 | // Ny Rate formel + distance mellem goal og new state (evt. manhattan). |
147 | // dist(new, goal) / dist(source, goal) |
148 | if (logger.isDebugEnabled()) { |
149 | logger.debug("calculateDesire(State state=" + state |
150 | + ", Step step=" + step + ") - end - return value=" |
151 | + returndouble); |
152 | } |
153 | return returndouble; |
154 | } |
155 | |
156 | /** |
157 | * Calculates the photosynthesis rate for a given state. |
158 | * @param state the state |
159 | * @return the rate of photosynthesis |
160 | */ |
161 | private double calculateRate(State state) { |
162 | //Retrieve necessary state |
163 | double shade = 0; |
164 | double sun = getSensorValue(state, SystemSettings.getIrradianceID()); |
165 | double temperature = getSensorValue(state, SystemSettings |
166 | .getTemperatureID()); |
167 | double co2 = getSensorValue(state, SystemSettings.getCO2ID()); |
168 | LeafPhotosynthesisModel model = new LeafPhotosynthesisModel(); |
169 | //Calculate |
170 | double currentPhotosynthesis = model.calculate(temperature, co2, sun, |
171 | GLASS_FACTOR, SHADE_FACTOR, shade); |
172 | double maxPhotosynthesis = calculateMax(shade, sun, |
173 | new ArrayList<ArrayList<Double>>()); |
174 | double rate = currentPhotosynthesis / maxPhotosynthesis; |
175 | return rate; |
176 | } |
177 | |
178 | /** |
179 | * Method calculates the maximum photosynthesis level. All calculated values |
180 | * are stored in the input matrix. |
181 | * <p> |
182 | * The format of the matrix is: |
183 | * |
184 | * <pre> |
185 | * Rows = temperature interval |
186 | * Columns = CO2 level |
187 | * |
188 | * [t1=[co2_level_1, co2_level_2, co2_level_3, ... , co2_level_n]] |
189 | * [t2=[co2_level_1, co2_level_2, co2_level_3, ... , co2_level_n]] |
190 | * [t3=[co2_level_1, co2_level_2, co2_level_3, ... , co2_level_n]] |
191 | * ... |
192 | * [tm=[co2_level_1, co2_level_2, co2_level_3, ... , co2_level_n]] |
193 | * |
194 | * Where: |
195 | * CO2_START < n < CO2_END, and |
196 | * T_START < m < T_END |
197 | * |
198 | * Increments: |
199 | * Temperature increment = 1 |
200 | * CO2 increment = CO2_INCREMENT |
201 | * |
202 | * </pre> |
203 | * |
204 | * @param shade |
205 | * screen setpoint value |
206 | * @param sun |
207 | * irradiance level |
208 | * @param matrix |
209 | * the matrix to be filled with all calculated values |
210 | * @return maximum photosynthesis value |
211 | */ |
212 | double calculateMax(double shade, double sun, |
213 | ArrayList<ArrayList<Double>> matrix) { |
214 | if (logger.isDebugEnabled()) { |
215 | logger.debug("calculateMax(double shade=" + shade + ", double sun=" |
216 | + sun + ", ArrayList<ArrayList<Double>> matrix=" + matrix |
217 | + ") - start"); |
218 | } |
219 | |
220 | LeafPhotosynthesisModel model = new LeafPhotosynthesisModel(); |
221 | double max = 0; |
222 | for (int temperature = T_START; temperature < T_END; temperature++) { |
223 | ArrayList<Double> inner = new ArrayList<Double>(); |
224 | for (int co2 = CO2_START; co2 < CO2_END; co2 += CO2_INCREMENT) { |
225 | // updating the counter |
226 | calculationCounter++; |
227 | double val = model.calculate(temperature, co2, sun, |
228 | GLASS_FACTOR, SHADE_FACTOR, shade); |
229 | inner.add(val); |
230 | max = Math.max(val, max); |
231 | } |
232 | matrix.add(inner); |
233 | } |
234 | |
235 | if (logger.isDebugEnabled()) { |
236 | logger.debug("calculateMax(double shade=" + shade + ", double sun=" |
237 | + sun + ", ArrayList<ArrayList<Double>> matrix=" + matrix |
238 | + ") - end - return value=" + max); |
239 | } |
240 | return max; |
241 | } |
242 | |
243 | /* |
244 | * (non-Javadoc) |
245 | * |
246 | * @see dk.deepthought.sidious.rules.Rule#getGoals() |
247 | */ |
248 | public Collection<Goal> getGoals() { |
249 | if (logger.isDebugEnabled()) { |
250 | logger.debug("getGoals() - start"); |
251 | } |
252 | double shade = getAdjustableSettingFromParent(SystemSettings |
253 | .getScreenSetPointID()); |
254 | double sun = ServiceEngine.getSensorValue(SystemSettings |
255 | .getIrradianceID()); |
256 | ArrayList<ArrayList<Double>> matrix = new ArrayList<ArrayList<Double>>(); |
257 | double max = calculateMax(shade, sun, matrix); |
258 | List<Goal> goals = new ArrayList<Goal>(); |
259 | for (int i = 0; i < matrix.size(); i++) { |
260 | ArrayList<Double> inner = matrix.get(i); |
261 | for (int j = 0; j < inner.size(); j++) { |
262 | double photosynthesis = inner.get(j); |
263 | if (photosynthesis / max >= DESIRED_RATE) { |
264 | SensorInput temperatureSensor = new SensorInput( |
265 | SystemSettings.getTemperatureID(), i + T_START); |
266 | SensorInput co2Sensor = new SensorInput(SystemSettings |
267 | .getCO2ID(), (j * CO2_INCREMENT) + CO2_START); |
268 | SensorInput sunSensor = new SensorInput(SystemSettings |
269 | .getIrradianceID(), sun); |
270 | |
271 | State goalState = new ClimaticState(Arrays.asList( |
272 | temperatureSensor, sunSensor, co2Sensor)); |
273 | Adjustable screenSetPoint = new ScreenSetPoint(shade); |
274 | Step step = new Step(Arrays.asList(screenSetPoint)); |
275 | double desire = desire(ServiceEngine.getCurrentState(), |
276 | goalState, step); |
277 | goals.add(new Goal(goalState, tweakValue * (DESIRED_RATE-desire), getParentID())); |
278 | } |
279 | } |
280 | } |
281 | return goals; |
282 | } |
283 | |
284 | /** |
285 | * Gets the amount of calculations made by this. |
286 | * @return the calculationCounter |
287 | */ |
288 | public static long getCalculationCounter() { |
289 | return calculationCounter; |
290 | } |
291 | |
292 | |
293 | } |