1 /*
2  *  recording_backend.h
3  *
4  *  This file is part of NEST.
5  *
6  *  Copyright (C) 2004 The NEST Initiative
7  *
8  *  NEST is free software: you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation, either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  NEST is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with NEST.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22 
23 #ifndef RECORDING_BACKEND_H
24 #define RECORDING_BACKEND_H
25 
26 // C++ includes:
27 #include <vector>
28 
29 // Includes from sli:
30 #include "dictdatum.h"
31 #include "name.h"
32 
33 namespace nest
34 {
35 
36 class RecordingDevice;
37 class Event;
38 
39 /**
40  * Abstract base class for all NESTio recording backends
41  *
42  * This class provides the interface for the NESTio recording backends
43  * with which `RecordingDevice`s can be enrolled for recording and
44  * which they can use to write their data.
45  *
46  * Built-in recording backends are registered in the constructor of
47  * IOManager by inserting an instance of each of them into a std::map
48  * under the name of the backend.
49  *
50  * A user level call to Simulate internally executes the sequence
51  * Prepare → Run → Cleanup.  During Prepare, the prepare() function of
52  * each backend is called by the IOManager. This gives the backend an
53  * opportunity to prepare data structures for the upcoming
54  * simulation cycle.
55  *
56  * The user level function Run drives the simulation main loop by
57  * uptating all nodes. At its beginning it calls pre_run_hook() on
58  * each recording backend via the IOManager. At the end of each run,
59  * it calls post_run_hook() respectively.
60  *
61  * During the simulation, recording devices call IOManager::write() in
62  * order to record data. These calls are forwarded to the backend, the
63  * device is enrolled with. Cleanup on the user level finally calls
64  * the cleanup() function of all backends.
65  *
66  * @ingroup NESTio
67 */
68 
69 class RecordingBackend
70 {
71 public:
RecordingBackend()72   RecordingBackend()
73   {
74   }
75 
~RecordingBackend()76   virtual ~RecordingBackend() throw()
77   {
78   }
79 
80   virtual void initialize() = 0;
81   virtual void finalize() = 0;
82 
83   /**
84    * Enroll a `RecordingDevice` with the `RecordingBackend`.
85    *
86    * When this function is called by a `RecordingDevice` @p device,
87    * the `RecordingBackend` can set up per-device data structures and
88    * properties. Individual device instances can be identified using
89    * the `thread` and `node_id` of the @p device.
90    *
91    * This function is called from the set_initialized_() function of
92    * the @p device and their set_status() function. The companion
93    * function @p set_value_names() is called from Node::pre_run_hook()
94    * and makes the names of values to be recorded known.
95    *
96    * A backend needs to be able to cope with multiple calls to this
97    * function, as multiple calls to set_status() may occur on the @p
98    * device. For already enrolled devices this usually means that only
99    * the parameters in @p params have to be set, but no further
100    * actions are needed.
101    *
102    * Each recording backend must ensure that enrollment (including all
103    * settings made by the user) is persistent over multiple calls to
104    * Prepare, while the enrollment of all devices should end with a
105    * call to finalize().
106    *
107    * A common implementation of this function will create an entry in
108    * a thread-local map, associating the device's node ID with the
109    * device-specific backend properties and an output facility of some
110    * kind.
111    *
112    * @param device the RecordingDevice to be enrolled
113    * @param params device-specific backend parameters
114    *
115    * @see set_value_names(), disenroll(), write(),
116    *
117    * @ingroup NESTio
118    */
119   virtual void enroll( const RecordingDevice& device, const DictionaryDatum& params ) = 0;
120 
121   /**
122    * Disenroll a `RecordingDevice` from the `RecordingBackend`.
123    *
124    * This function is considered to be the opposite of enroll() in the
125    * sense that it cancels the enrollment of a RecordingDevice from a
126    * RecordingBackend by deleting all device specific data. When
127    * setting a new recording backend for a recording device, this
128    * function is called for each backend the device is not enrolled
129    * with.
130    *
131    * @param device the RecordingDevice to be disenrolled
132    *
133    * @see enroll()
134    *
135    * @ingroup NESTio
136    */
137   virtual void disenroll( const RecordingDevice& device ) = 0;
138 
139   /**
140    * To make the names of recorded quantities known to the
141    * `RecordingBackend`, the vectors @p double_value_names and @p
142    * long_value_names can be set appropriately. If no values of a
143    * certain type (or none at all) will be recorded by @p device, the
144    * constants @ref NO_DOUBLE_VALUE_NAMES and @ref NO_LONG_VALUE_NAMES
145    * can be used. Please note that the lengths of the value names
146    * vectors *must* correspond to the length of the data vectors
147    * written during calls to `write()`, although this is not enforced
148    * by the API.
149    *
150    * @param device the device to set the value names for
151    * @param double_value_names the names for double values to be recorded
152    * @param long_value_names the names for long values to be recorded
153    *
154    * @see enroll(), disenroll(), write(),
155    *
156    * @ingroup NESTio
157    */
158   virtual void set_value_names( const RecordingDevice& device,
159     const std::vector< Name >& double_value_names,
160     const std::vector< Name >& long_value_names ) = 0;
161 
162   /**
163    * Prepare the backend at begin of the NEST Simulate function.
164    *
165    * This function is called by `KernelManager::prepare()` and allows the
166    * backend to open files or establish network connections or take similar
167    * action.
168    *
169    * @see cleanup()
170    *
171    * @ingroup NESTio
172    */
173   virtual void prepare() = 0;
174 
175   /**
176   * Clean up the backend at the end of a user level call to the NEST Simulate
177   * function.
178   *
179   * This function is called by `SimulationManager::cleanup()` and allows the
180   * backend to close open files or network connections or take similar action.
181   *
182   * @see prepare()
183   *
184   * @ingroup NESTio
185   */
186   virtual void cleanup() = 0;
187 
188   /**
189    * Initialize global backend-specific data structures.
190    *
191    * This function is called on each backend right at the very beginning of
192    * `SimulationManager::run()`. It can be used for initializations which have
193    * to be repeated at the beginning of every single call to run in a
194    * prepare-run-run-...-run-run-cleanup sequence.
195    *
196    * @see post_run_hook()
197    *
198    * @ingroup NESTio
199    */
200   virtual void pre_run_hook() = 0;
201 
202   /**
203    * Clean up the backend at the end of a Run.
204    *
205    * This is called right before `SimulationManager::run()` terminates. It
206    * allows the backend to flush open files, write remaining data to the
207    * screen, or perform similar operations that make sure that the user
208    * has access to all data from the previous simulation run.
209    *
210    * @see pre_run_hook()
211    *
212    * @ingroup NESTio
213    */
214   virtual void post_run_hook() = 0;
215 
216   /**
217    * Do work required at the end of each simulation step.
218    *
219    * This is called at the very end of each simulation step. It can for example
220    * be used to carry out writing to files in a synchronized way, all threads
221    * on all MPI processes performing it at the same time.
222    *
223    * @see pre_run_hook()
224    *
225    * @ingroup NESTio
226    */
227   virtual void post_step_hook() = 0;
228 
229   /**
230    * Write the data from the event to the backend specific channel together
231    * with the values given.
232    *
233    * This function needs to respect the time_in_steps property of the device
234    * and should return as quickly as possible if the `RecordingDevice` @p device
235    * is not enrolled with the backend.
236    *
237    * @param device the RecordingDevice, backend-specific channel to write to
238    * @param event the event
239    * @param double_values vector of double valued to be written
240    * @param long_values vector of long values to be written
241    *
242    * @ingroup NESTio
243    */
244   virtual void write( const RecordingDevice& device,
245     const Event& event,
246     const std::vector< double >& double_values,
247     const std::vector< long >& long_values ) = 0;
248 
249   /**
250    * Set the status of the recording backend using the key-value pairs
251    * contained in the params dictionary.
252    *
253    * @param params the status of the recording backend
254    *
255    * @see get_status()
256    *
257    * @ingroup NESTio
258    */
259   virtual void set_status( const DictionaryDatum& params ) = 0;
260 
261   /**
262    * Return the status of the recording backend by writing it to the given
263    * params dictionary.
264    *
265    * @param params the status of the recording backend
266    *
267    * @see set_status()
268    *
269    * @ingroup NESTio
270    */
271   virtual void get_status( DictionaryDatum& params ) const = 0;
272 
273   /**
274    * Check if the given per-device properties are valid and usable by
275    * the backend.
276    *
277    * This function is used to validate properties when SetDefaults is
278    * called on a recording device. If the properties are found to be
279    * valid, they will be cached in the recording device and set for
280    * individual instances by means of the call to enroll from the
281    * device's set_initialized_() function. In case the properties are
282    * invalid, this function is expected to throw BadProperty.
283    *
284    * @param params the parameter dictionary to validate
285    *
286    * @see get_device_defaults(), get_device_status()
287    *
288    * @ingroup NESTio
289    */
290   virtual void check_device_status( const DictionaryDatum& params ) const = 0;
291 
292   /**
293    * Return the per-device defaults by writing it to the given params
294    * dictionary.
295    *
296    * @param params the dictionary to add device-specific backend parameters to
297    *
298    * @see check_device_status(), get_device_status()
299    *
300    * @ingroup NESTio
301    */
302   virtual void get_device_defaults( DictionaryDatum& params ) const = 0;
303 
304   /**
305    * Return the per-device status of the given recording device by
306    * writing it to the given params dictionary.
307    *
308    * Please note that a corresponding setter function does not exist.
309    * Device-specific backend parameters are given in the call to
310    * enroll.
311    *
312    * @param device the recording device for which the status is returned
313    * @param params the dictionary to add device-specific backend parameters to
314    *
315    * @see enroll(), check_device_status(), get_device_defaults()
316    *
317    * @ingroup NESTio
318    */
319   virtual void get_device_status( const RecordingDevice& device, DictionaryDatum& params ) const = 0;
320 
321   static const std::vector< Name > NO_DOUBLE_VALUE_NAMES;
322   static const std::vector< Name > NO_LONG_VALUE_NAMES;
323   static const std::vector< double > NO_DOUBLE_VALUES;
324   static const std::vector< long > NO_LONG_VALUES;
325 };
326 
327 } // namespace
328 
329 #endif // RECORDING_BACKEND_H
330