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_RATE0.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 &lt; n &lt; CO2_END, and 
196      *   T_START &lt; m &lt; 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 }