001 package dk.deepthought.sidious.rules;
002
003 import java.util.ArrayList;
004 import java.util.Arrays;
005 import java.util.Collection;
006 import java.util.List;
007
008 import org.apache.commons.logging.Log;
009 import org.apache.commons.logging.LogFactory;
010
011 import dk.deepthought.sidious.goalhandler.Goal;
012 import dk.deepthought.sidious.greenhouse.ClimaticState;
013 import dk.deepthought.sidious.greenhouse.LeafPhotosynthesisModel;
014 import dk.deepthought.sidious.greenhouse.ScreenSetPoint;
015 import dk.deepthought.sidious.greenhouse.SensorInput;
016 import dk.deepthought.sidious.services.ServiceEngine;
017 import dk.deepthought.sidious.supportsystem.Adjustable;
018 import dk.deepthought.sidious.supportsystem.State;
019 import dk.deepthought.sidious.supportsystem.Step;
020 import dk.deepthought.sidious.supportsystem.SuperLinkID;
021 import dk.deepthought.sidious.supportsystem.SystemSettings;
022 import dk.deepthought.sidious.util.RuleProperty;
023
024 /**
025 * Class represents a photosynthesis rule.
026 * <p>
027 * It is responsible for maintaining a desired photosynthesis rate.
028 *
029 * @author Deepthought
030 *
031 */
032 public class PhotosynthesisRule extends Rule {
033 /**
034 * Logger for this class
035 */
036 private static final Log logger = LogFactory
037 .getLog(PhotosynthesisRule.class);
038
039 /**
040 * The RuleProperty of this class
041 */
042 private static RuleProperty ruleProperty;
043
044 /**
045 * Value is defaulted in the calculate method.
046 */
047 private static final double GLASS_FACTOR = Double.NaN;
048
049 /**
050 * Value is defaulted in the calculate method.
051 */
052 private static final double SHADE_FACTOR = Double.NaN;
053
054 // TODO extract into rule property file.
055 static final int T_START = 18;
056
057 // TODO extract into rule property file.
058 static final int CO2_START = 300;
059
060 // TODO extract into rule property file.
061 static final int CO2_INCREMENT = 100;
062
063 // TODO extract into rule property file.
064 private static final int T_END = 36;
065
066 // TODO extract into rule property file.
067 private static final int CO2_END = 1600;
068
069 // TODO extract into rule property file.
070 private static final double DESIRED_RATE = 0.8;
071
072 /**
073 * Value sets the importance of this rule.
074 * TODO extract into rule property file
075 */
076 private double tweakValue = 1;
077
078 /**
079 * Used to keep track of the amount of model calculations done by this
080 */
081 private static long calculationCounter = 0;
082
083
084 public PhotosynthesisRule(final SuperLinkID parentID) {
085 if (ruleProperty == null) {
086 ruleProperty = new RuleProperty(this.getClass().getSimpleName());
087 }
088 setParentID(parentID);
089 }
090
091 /*
092 * (non-Javadoc)
093 *
094 * @see dk.deepthought.sidious.rules.Rule#desire(dk.deepthought.sidious.supportsystem.State,
095 * dk.deepthought.sidious.supportsystem.State, dk.deepthought.sidious.supportsystem.Step)
096 */
097 public double desire(State currentState, State newState, Step step) {
098 if (logger.isDebugEnabled()) {
099 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 }
|