NEST main@caf0ae8
 
Loading...
Searching...
No Matches
universal_data_logger_impl.h
Go to the documentation of this file.
1/*
2 * universal_data_logger_impl.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 UNIVERSAL_DATA_LOGGER_IMPL_H
24#define UNIVERSAL_DATA_LOGGER_IMPL_H
25
27
28// Includes from nestkernel:
30#include "kernel_manager.h"
31#include "nest_time.h"
32#include "node.h"
33
34namespace nest
35{
36
37template < typename HostNode >
38DynamicUniversalDataLogger< HostNode >::DynamicUniversalDataLogger( HostNode& host )
39 : host_( host )
40 , data_loggers_()
41{
42}
43
44template < typename HostNode >
45void
46DynamicUniversalDataLogger< HostNode >::reset()
47{
48 for ( DLiter_ it = data_loggers_.begin(); it != data_loggers_.end(); ++it )
49 {
50 it->reset();
51 }
52}
53
54template < typename HostNode >
55void
56DynamicUniversalDataLogger< HostNode >::init()
57{
58 for ( DLiter_ it = data_loggers_.begin(); it != data_loggers_.end(); ++it )
59 {
60 it->init();
61 }
62}
63
64template < typename HostNode >
65void
66DynamicUniversalDataLogger< HostNode >::record_data( long step )
67{
68 for ( DLiter_ it = data_loggers_.begin(); it != data_loggers_.end(); ++it )
69 {
70 it->record_data( host_, step );
71 }
72}
73
74template < typename HostNode >
75void
76DynamicUniversalDataLogger< HostNode >::handle( const DataLoggingRequest& dlr )
77{
78 const size_t rport = dlr.get_rport();
79 assert( rport >= 1 );
80 assert( static_cast< size_t >( rport ) <= data_loggers_.size() );
81 data_loggers_[ rport - 1 ].handle( host_, dlr );
82}
83
84template < typename HostNode >
85void
86DynamicUniversalDataLogger< HostNode >::DataLogger_::reset()
87{
88 data_.clear();
89 next_rec_step_ = -1; // flag as uninitialized
90}
91
92template < typename HostNode >
93void
94DynamicUniversalDataLogger< HostNode >::DataLogger_::init()
95{
96 if ( num_vars_ < 1 )
97 {
98 return;
99 } // not recording anything
100
101 // Next recording step is in current slice or beyond, indicates that
102 // buffer is properly initialized.
103 if ( next_rec_step_ >= kernel().simulation_manager.get_slice_origin().get_steps() )
104 {
105 return;
106 }
107
108 // If we get here, the buffer has either never been initialized or has been dormant
109 // during a period when the host node was frozen. We then (re-)initialize.
110 data_.clear();
111
112 // store recording time in steps
113 rec_int_steps_ = recording_interval_.get_steps();
114
115 // set next recording step to first multiple of rec_int_steps_
116 // beyond current time, shifted one to left, since rec_step marks
117 // left of update intervals, and we want time stamps at right end of
118 // update interval to be multiples of recording interval. Need to add
119 // +1 because the division result is rounded down.
120 next_rec_step_ = ( kernel().simulation_manager.get_time().get_steps() / rec_int_steps_ + 1 ) * rec_int_steps_ - 1;
121
122 // If offset is not 0, adjust next recording step to account for it by first setting next recording
123 // step to be offset and then iterating until the variable is greater than current simulation time.
124 if ( recording_offset_.get_steps() != 0 )
125 {
126 next_rec_step_ = recording_offset_.get_steps() - 1; // shifted one to left
127 while ( next_rec_step_ <= kernel().simulation_manager.get_time().get_steps() )
128 {
129 next_rec_step_ += rec_int_steps_;
130 }
131 }
132
133 // number of data points per slice
134 const long recs_per_slice = static_cast< long >(
135 std::ceil( kernel().connection_manager.get_min_delay() / static_cast< double >( rec_int_steps_ ) ) );
136
137 data_.resize( 2, DataLoggingReply::Container( recs_per_slice, DataLoggingReply::Item( num_vars_ ) ) );
138
139 next_rec_.resize( 2 ); // just for safety's sake
140 next_rec_[ 0 ] = next_rec_[ 1 ] = 0; // start at beginning of buffer
141}
142
143template < typename HostNode >
144void
145DynamicUniversalDataLogger< HostNode >::DataLogger_::record_data( const HostNode&, long step )
146{
147 if ( num_vars_ < 1 or step < next_rec_step_ )
148 {
149 return;
150 }
151
152 const size_t wt = kernel().event_delivery_manager.write_toggle();
153
154 assert( wt < next_rec_.size() );
155 assert( wt < data_.size() );
156
157 // The following assertion may fire if the multimeter connected to
158 // this logger is frozen. In that case, handle() is not called and
159 // next_rec_[wt] never reset. The assert() prevents error propagation.
160 // This is not an exception, since I consider the chance of users
161 // freezing multimeters very slim.
162 // See #464 for details.
163 assert( next_rec_[ wt ] < data_[ wt ].size() );
164
165 DataLoggingReply::Item& dest = data_[ wt ][ next_rec_[ wt ] ];
166
167 // set time stamp: step is left end of update interval, so add 1
168 dest.timestamp = Time::step( step + 1 );
169
170 // obtain data through access functions, calling via pointer-to-member
171 for ( size_t j = 0; j < num_vars_; ++j )
172 {
173 dest.data[ j ] = ( *( node_access_[ j ] ) )();
174 }
175
176 next_rec_step_ += rec_int_steps_;
177
178 // We just increment. Construction ensures that we cannot overflow,
179 // and read-out resets.
180 // Overflow is possible if the multimeter is frozen, see #464.
181 // In that case, the assertion above will trigger.
182 ++next_rec_[ wt ];
183}
184
185template < typename HostNode >
186void
187DynamicUniversalDataLogger< HostNode >::DataLogger_::handle( HostNode& host, const DataLoggingRequest& request )
188{
189 if ( num_vars_ < 1 )
190 {
191 return;
192 } // nothing to do
193
194 // The following assertions will fire if the user forgot to call init()
195 // on the data logger.
196 assert( next_rec_.size() == 2 );
197 assert( data_.size() == 2 );
198
199 // get read toggle and start and end of slice
200 const size_t rt = kernel().event_delivery_manager.read_toggle();
201 assert( not data_[ rt ].empty() );
202
203 // Check if we have valid data, i.e., data with time stamps within the
204 // past time slice. This may not be the case if the node has been frozen.
205 // In that case, we still reset the recording marker, to prepare for the next round.
206 if ( data_[ rt ][ 0 ].timestamp <= kernel().simulation_manager.get_previous_slice_origin() )
207 {
208 next_rec_[ rt ] = 0;
209 return;
210 }
211
212 // If recording interval and min_delay are not commensurable,
213 // the last entry of data_ will not contain useful data for every
214 // other slice. We mark this by time stamp -infinity.
215 // Applying this mark here is less work than initializing all time stamps
216 // to -infinity after each call to this function.
217 if ( next_rec_[ rt ] < data_[ rt ].size() )
218 {
219 data_[ rt ][ next_rec_[ rt ] ].timestamp = Time::neg_inf();
220 }
221
222 // now create reply event and rigg it
223 DataLoggingReply reply( data_[ rt ] );
224
225 // "clear" data
226 next_rec_[ rt ] = 0;
227
228 reply.set_sender( host );
229 reply.set_sender_node_id( host.get_node_id() );
230 reply.set_receiver( request.get_sender() );
231 reply.set_port( request.get_port() );
232
233 // send it off
235}
236
237template < typename HostNode >
238UniversalDataLogger< HostNode >::UniversalDataLogger( HostNode& host )
239 : host_( host )
240 , data_loggers_()
241{
242}
243
244template < typename HostNode >
245void
246UniversalDataLogger< HostNode >::reset()
247{
248 for ( DLiter_ it = data_loggers_.begin(); it != data_loggers_.end(); ++it )
249 {
250 it->reset();
251 }
252}
253
254template < typename HostNode >
255void
256UniversalDataLogger< HostNode >::init()
257{
258 for ( DLiter_ it = data_loggers_.begin(); it != data_loggers_.end(); ++it )
259 {
260 it->init();
261 }
262}
263
264template < typename HostNode >
265void
266UniversalDataLogger< HostNode >::record_data( long step )
267{
268 for ( DLiter_ it = data_loggers_.begin(); it != data_loggers_.end(); ++it )
269 {
270 it->record_data( host_, step );
271 }
272}
273
274template < typename HostNode >
275void
276UniversalDataLogger< HostNode >::handle( const DataLoggingRequest& dlr )
277{
278 const size_t rport = dlr.get_rport();
279 assert( rport >= 1 );
280 assert( static_cast< size_t >( rport ) <= data_loggers_.size() );
281 data_loggers_[ rport - 1 ].handle( host_, dlr );
282}
283
284template < typename HostNode >
285void
286UniversalDataLogger< HostNode >::DataLogger_::reset()
287{
288 data_.clear();
289 next_rec_step_ = -1; // flag as uninitialized
290}
291
292template < typename HostNode >
293void
294UniversalDataLogger< HostNode >::DataLogger_::init()
295{
296 if ( num_vars_ < 1 )
297 {
298 // not recording anything
299 return;
300 }
301
302 // Next recording step is in current slice or beyond, indicates that
303 // buffer is properly initialized.
304 if ( next_rec_step_ >= kernel().simulation_manager.get_slice_origin().get_steps() )
305 {
306 return;
307 }
308
309 // If we get here, the buffer has either never been initialized or has
310 // been dormant during a period when the host node was frozen. We then (re-)initialize.
311 data_.clear();
312
313 // store recording time in steps
314 rec_int_steps_ = recording_interval_.get_steps();
315
316 // set next recording step to first multiple of rec_int_steps_
317 // beyond current time, shifted one to left, since rec_step marks
318 // left of update intervals, and we want time stamps at right end of
319 // update interval to be multiples of recording interval. Need to add
320 // +1 because the division result is rounded down.
321 next_rec_step_ = ( kernel().simulation_manager.get_time().get_steps() / rec_int_steps_ + 1 ) * rec_int_steps_ - 1;
322
323 // If offset is not 0, adjust next recording step to account for it by first setting next recording
324 // step to be offset and then iterating until the variable is greater than current simulation time.
325 if ( recording_offset_.get_steps() != 0 )
326 {
327 next_rec_step_ = recording_offset_.get_steps() - 1; // shifted one to left
328 while ( next_rec_step_ <= kernel().simulation_manager.get_time().get_steps() )
329 {
330 next_rec_step_ += rec_int_steps_;
331 }
332 }
333
334 // number of data points per slice
335 const long recs_per_slice = static_cast< long >(
336 std::ceil( kernel().connection_manager.get_min_delay() / static_cast< double >( rec_int_steps_ ) ) );
337
338 data_.resize( 2, DataLoggingReply::Container( recs_per_slice, DataLoggingReply::Item( num_vars_ ) ) );
339
340 next_rec_.resize( 2 ); // just for safety's sake
341 next_rec_[ 0 ] = next_rec_[ 1 ] = 0; // start at beginning of buffer
342}
343
344template < typename HostNode >
345void
346UniversalDataLogger< HostNode >::DataLogger_::record_data( const HostNode& host, long step )
347{
348 if ( num_vars_ < 1 or step < next_rec_step_ )
349 {
350 return;
351 }
352
353 const size_t wt = kernel().event_delivery_manager.write_toggle();
354
355 assert( wt < next_rec_.size() );
356 assert( wt < data_.size() );
357
358 // The following assertion may fire if the multimeter connected to
359 // this logger is frozen. In that case, handle() is not called and
360 // next_rec_[wt] never reset. The assert() prevents error propagation.
361 // This is not an exception, since I consider the chance of users
362 // freezing multimeters very slim.
363 // See #464 for details.
364 assert( next_rec_[ wt ] < data_[ wt ].size() );
365
366 DataLoggingReply::Item& dest = data_[ wt ][ next_rec_[ wt ] ];
367
368 // set time stamp: step is left end of update interval, so add 1
369 dest.timestamp = Time::step( step + 1 );
370
371 // obtain data through access functions, calling via pointer-to-member
372 for ( size_t j = 0; j < num_vars_; ++j )
373 {
374 dest.data[ j ] = ( ( host ).*( node_access_[ j ] ) )();
375 }
376
377 next_rec_step_ += rec_int_steps_;
378
379 // We just increment. Construction ensures that we cannot overflow,
380 // and read-out resets.
381 // Overflow is possible if the multimeter is frozen, see #464.
382 // In that case, the assertion above will trigger.
383 ++next_rec_[ wt ];
384}
385
386template < typename HostNode >
387void
388UniversalDataLogger< HostNode >::DataLogger_::handle( HostNode& host, const DataLoggingRequest& request )
389{
390 if ( num_vars_ < 1 )
391 {
392 // nothing to do
393 return;
394 }
395
396 // The following assertions will fire if the user forgot to call init()
397 // on the data logger.
398 assert( next_rec_.size() == 2 );
399 assert( data_.size() == 2 );
400
401 // get read toggle and start and end of slice
402 const size_t rt = kernel().event_delivery_manager.read_toggle();
403 assert( not data_[ rt ].empty() );
404
405 // Check if we have valid data, i.e., data with time stamps within the
406 // past time slice. This may not be the case if the node has been frozen.
407 // In that case, we still reset the recording marker, to prepare for the next round.
408 if ( data_[ rt ][ 0 ].timestamp <= kernel().simulation_manager.get_previous_slice_origin() )
409 {
410 next_rec_[ rt ] = 0;
411 return;
412 }
413
414 // If recording interval and min_delay are not commensurable,
415 // the last entry of data_ will not contain useful data for every
416 // other slice. We mark this by time stamp -infinity.
417 // Applying this mark here is less work than initializing all time stamps
418 // to -infinity after each call to this function.
419 if ( next_rec_[ rt ] < data_[ rt ].size() )
420 {
421 data_[ rt ][ next_rec_[ rt ] ].timestamp = Time::neg_inf();
422 }
423
424 // now create reply event and rigg it
425 DataLoggingReply reply( data_[ rt ] );
426
427 // "clear" data
428 next_rec_[ rt ] = 0;
429
430 reply.set_sender( host );
431 reply.set_sender_node_id( host.get_node_id() );
432 reply.set_receiver( request.get_sender() );
433 reply.set_port( request.get_port() );
434
435 // send it off
437}
438
439} // namespace nest
440
441#endif /* #ifndef UNIVERSAL_DATA_LOGGER_IMPL_H */
size_t read_toggle() const
Return 1 - write_toggle().
Definition event_delivery_manager.h:520
void send_to_node(Event &e)
Send event e directly to its target node.
Definition event_delivery_manager.h:502
size_t write_toggle() const
Return 0 for even, 1 for odd time slices.
Definition event_delivery_manager_impl.h:173
Time const get_time() const
Precise time of simulation.
Definition simulation_manager.h:256
long get_steps() const
Definition nest_time.h:504
EventDeliveryManager event_delivery_manager
Definition kernel_manager.h:241
SimulationManager simulation_manager
Definition kernel_manager.h:237
const std::string rport("receptor")
Namespace for the NEST simulation kernel.
Definition beta_normalization_factor.h:33
KernelManager & kernel()
Definition kernel_manager.h:311
Declarations for base class Node.