View Javadoc

1   package org.lcsim.util;
2   
3   import java.io.PrintStream;
4   import java.util.ArrayList;
5   import java.util.List;
6   import java.util.Random;
7   import java.util.logging.Level;
8   import java.util.logging.Logger;
9   
10  import org.lcsim.conditions.ConditionsManager;
11  import org.lcsim.event.EventHeader;
12  import org.lcsim.geometry.Detector;
13  
14  /**
15   * A driver is a steering routine which can deal with event processing, and/or call any number of child drivers. 
16   * When used as a child driver all parameters such as histogramLevel and logger are inherited from the parent driver.
17   * <p>
18   * It also allows controlling the histogram level of the processors being called and handles coordination of random numbers 
19   * between Monte Carlo processors.
20   *
21   * @author Tony Johnson
22   * @version $Id: Driver.java,v 1.14 2007/09/11 00:21:00 tonyj Exp $
23   */
24  
25  public class Driver {
26      
27      // We dont use an enum, because we want to be able to test for level>some value.
28      // and because drivers can use special values for special purposes.
29      public final static int HLEVEL_DEFAULT = -1;
30      public final static int HLEVEL_OFF = 0;
31      public final static int HLEVEL_NORMAL = 1;
32      public final static int HLEVEL_HIGH = 3;
33      public final static int HLEVEL_FULL = 5;
34  
35      private static Driver mother = new MotherOfAllDrivers();
36      private final List<Driver> subDrivers = new ArrayList<Driver>();
37      private Driver parent = mother;
38      private int histogramLevel = HLEVEL_DEFAULT;
39      private Random random;
40      private final String driverName;
41      private int nEvents;
42      private long nNanos;
43  
44      /**
45       * Creates a driver
46       */
47      public Driver() {
48          this(null);
49      }
50  
51      Driver(String name) {
52          if (name == null || name.length() == 0) {
53              String id = getClass().getName();
54              int pos = id.lastIndexOf('.');
55              driverName = id.substring(pos < 0 ? 0 : pos + 1);
56          } else {
57              driverName = name;
58          }
59      }
60  
61      /**
62       * Add a sub-Driver to this Driver. Sub-drivers are automatically called from the process method.
63       * @param driver The Driver to be added
64       */
65      public void add(Driver driver) {
66          subDrivers.add(driver);
67          driver.parent = this;
68      }
69  
70      /**
71       * Removes a sub-Driver from this Driver
72       * @param driver The Driver to be removed
73       */
74      public void remove(Driver driver) {
75          subDrivers.remove(driver);
76          driver.parent = mother;
77      }
78  
79      /**
80       * Returns a List of all the drivers added to this Driver
81       */
82      public List<Driver> drivers() {
83          return subDrivers;
84      }
85  
86      /**
87       * Tests to see if a given Driver is already a child of this Driver     
88       * @param driver Driver to be checked
89       */
90      public boolean contains(Driver driver) {
91          return subDrivers.contains(driver);
92      }
93  
94      /**
95       * Returns a logger for logging diagnostic messages from this driver
96       */
97      public Logger getLogger() {
98          //return Logger.getLogger(pathToMother());
99          return Logger.getLogger(getClass().getName());
100     }
101 
102     /**
103      * Get the name of this driver. Normally this will be the class name of the driver (without the packaging information).
104      */
105     public String getName() {
106         return driverName;
107     }
108 
109     String pathToMother() {
110         return parent.pathToMother() + "." + driverName;
111     }
112 
113     /**
114      * Get the default histogram level for this driver
115      */
116     public int getHistogramLevel() {
117         return histogramLevel <= HLEVEL_DEFAULT ? parent.getHistogramLevel() : histogramLevel;
118     }
119 
120     /**
121      * Set the histogram level for this driver (and its child drivers)
122      */
123     public void setHistogramLevel(int level) {
124         histogramLevel = level;
125     }
126 
127     public ConditionsManager getConditionsManager() {
128         return parent.getConditionsManager();
129     }
130 
131     /**
132      * Called by the framework when event processing is suspended.
133      */
134     protected void suspend() {
135         for (Driver driver : subDrivers)
136             driver.suspend();
137     }
138 
139     /**
140      * Called by the framework when event processing is resumed.
141      */
142     protected void resume() {
143         for (Driver driver : subDrivers)
144             driver.resume();
145     }
146 
147     /**
148      * Called when all data processing is finished.
149      */
150     protected void endOfData() {
151         for (Driver driver : subDrivers)
152             driver.endOfData();
153     }
154 
155     /**
156      * Called before the first event is processed, or after a rewind.
157      */
158     protected void startOfData() {
159         for (Driver driver : subDrivers)
160             driver.startOfData();
161     }
162 
163     /**
164      * Called by the framework before process method when the detector geometry changes. This method is gauranteed to be called once before the first call to process.
165      * 
166      * @param Detector The new detector
167      */
168     protected void detectorChanged(Detector detector) {
169         for (Driver driver : subDrivers)
170             driver.detectorChanged(detector);
171     }
172 
173     /**
174      * Called by the framework to process an event. 
175      * Don't forget to call <code>super.process(event)</code> to cause the child processes to be executed. 
176      * In addition the process event call can throw some special exceptions:
177      * <ul>
178      * <li>NextEventException - aborts further processing of this event</li>
179      * <li>StopRunException - causes event processing to be stopped</li>
180      * </ul>
181      *
182      * @param event The event to be processed
183      * @see Driver.NextEventException
184      * @see Driver.AbortRunException
185      */
186     protected void process(EventHeader event) {
187         processChildren(event);
188     }
189 
190     /**
191      * Clear statistics
192      */
193     public void clearStatistics() {
194         nEvents = 0;
195         nNanos = 0;
196     }
197 
198     /**
199      * Print statistics for this driver and its children
200      */
201     public void printStatistics(PrintStream out) {
202         printStatistics(out, 0, 0);
203     }
204 
205     private void printStatistics(PrintStream out, int indent, long parentNanos) {
206         printStatisticsLine(out, indent, getName(), nEvents, nNanos, parentNanos);
207         if (!subDrivers.isEmpty()) {
208             int nIndent = indent + 1;
209             long self = nNanos;
210             for (Driver driver : subDrivers) {
211                 driver.printStatistics(out, nIndent, nNanos);
212                 self -= driver.nNanos;
213             }
214             printStatisticsLine(out, nIndent, "*self", nEvents, self, nNanos);
215         }
216     }
217 
218     private static void printStatisticsLine(PrintStream out, int indent, String name, int nEvents, long time, long parentTime) {
219         out.print(formatName(indent, name, 40));
220         out.print(' ');
221         out.print(nEvents);
222         out.print(' ');
223         out.printf(formatTime(time));
224         if (parentTime > 0) {
225             out.print(' ');
226             out.printf("%3.1f", 100. * time / parentTime);
227             out.print('%');
228         }
229         out.println();
230     }
231 
232     private static String formatName(int indent, String name, int width) {
233         StringBuilder builder = new StringBuilder();
234         for (int i = 0; i < indent; i++)
235             builder.append("   ");
236         builder.append(name);
237         if (builder.length() > width)
238             builder.setLength(width);
239         else
240             for (int i = builder.length(); i < width; i++)
241                 builder.append(' ');
242         return builder.toString();
243     }
244 
245     private static String formatTime(long nanos) {
246         String unit = "ms";
247         double time = nanos / 1000000.;
248         if (time > 1000) {
249             time /= 1000;
250             unit = "s";
251         }
252         java.util.Formatter formatter = new java.util.Formatter();
253         formatter.format("%3.3g", time);
254         formatter.format("%s", unit);
255         return formatter.toString();
256     }
257 
258     void doProcess(EventHeader event) {
259         nEvents++;
260         long start = System.nanoTime();
261         process(event);
262         long stop = System.nanoTime();
263         nNanos += (stop - start);
264     }
265 
266     /**
267      * Calls the sub-Drivers process() method. <b>Note:</b> This method is only public so that it can be called from Jython, see LCSIM-30
268      */
269     public void processChildren(EventHeader event) {
270         for (Driver driver : subDrivers)
271             driver.doProcess(event);
272     }
273 
274     public Random getRandom() {
275         return random == null ? parent.getRandom() : random;
276     }
277 
278     /**
279      * Set default random number generator for this driver and all child drivers.
280      * 
281      * @param random The random number generator, or <code>null</code> to reset to default
282      */
283     public void setRandom(Random random) {
284         this.random = random;
285     }
286 
287     public void setLogLevel(String logLevel) {
288         this.getLogger().setLevel(Level.parse(logLevel));
289     }
290 
291     // The only driver that does not have a parent
292     // This is used to set defaults for "inherited" items
293     private static class MotherOfAllDrivers extends Driver {
294         private Random random = new Random();
295 
296         MotherOfAllDrivers() {
297             super("TOP");
298         }
299 
300         public Random getRandom() {
301             return random;
302         }
303 
304         public int getHistogramLevel() {
305             return 0;
306         }
307 
308         public ConditionsManager getConditionsManager() {
309             return ConditionsManager.defaultInstance();
310         }
311 
312         String pathToMother() {
313             return getName();
314         }
315     }
316    
317     /**
318      * If thrown during the process method of a driver, causes processing of the current event to be aborted. Event procssing skips immediately to the next event.
319      */
320     public static class NextEventException extends RuntimeException {
321         public NextEventException() {
322             super("Next Event");
323         }
324     }
325 
326     /**
327      * If thrown during the process method of a driver, causes processing of events to be aborted.
328      */
329     public static class AbortRunException extends RuntimeException {
330         public AbortRunException() {
331             super("Abort Run");
332         }
333     }
334 }