rgsl/types/
n_tuples.rs

1//
2// A rust binding for the GSL library by Guillaume Gomez (guillaume1.gomez@gmail.com)
3//
4
5/*!
6# N-tuples
7
8This chapter describes functions for creating and manipulating ntuples, sets of values associated with events. The ntuples are stored in
9files. Their values can be extracted in any combination and booked in a histogram using a selection function.
10
11The values to be stored are held in a user-defined data structure, and an ntuple is created associating this data structure with a file.
12The values are then written to the file (normally inside a loop) using the ntuple functions described below.
13
14A histogram can be created from ntuple data by providing a selection function and a value function. The selection function specifies
15whether an event should be included in the subset to be analyzed or not. The value function computes the entry to be added to the histogram
16for each event.
17
18## Histogramming ntuple values
19
20Once an ntuple has been created its contents can be histogrammed in various ways using the function gsl_ntuple_project. Two user-defined
21functions must be provided, a function to select events and a function to compute scalar values. The selection function and the value
22function both accept the ntuple row as a first argument and other parameters as a second argument.
23
24The selection function determines which ntuple rows are selected for histogramming.
25
26## References and Further Reading
27
28Further information on the use of ntuples can be found in the documentation for the CERN packages PAW and HBOOK (available online).
29!*/
30
31use crate::Value;
32use ffi::FFI;
33use std::ffi::CString;
34use std::mem::MaybeUninit;
35use std::os::raw::{c_char, c_void};
36use std::path::Path;
37
38pub struct WriteNTuples {
39    n: *mut sys::gsl_ntuple,
40}
41
42impl WriteNTuples {
43    /// This function creates a new write-only ntuple file filename for ntuples of size size and
44    /// returns a pointer to the newly created ntuple struct. Any existing file with the same name
45    /// is truncated to zero length and overwritten. A pointer to memory for the current ntuple
46    /// row ntuple_data must be supplied-this is used to copy ntuples in and out of the file.
47    #[doc(alias = "gsl_ntuple_create")]
48    pub fn create<P: AsRef<Path>>(filename: P) -> Option<WriteNTuples> {
49        let filename = filename.as_ref();
50        let filename = filename.to_str().expect("Failed to convert path to str");
51        let c_str = CString::new(filename.as_bytes()).unwrap();
52        let tmp = unsafe {
53            sys::gsl_ntuple_create(c_str.as_ptr() as *mut c_char, std::ptr::null_mut(), 0)
54        };
55
56        if tmp.is_null() {
57            None
58        } else {
59            Some(Self { n: tmp })
60        }
61    }
62
63    /// This function writes the current ntuple ntuple->ntuple_data of size ntuple->size to the
64    /// corresponding file.
65    #[doc(alias = "gsl_ntuple_write")]
66    pub fn write<T: Sized>(&mut self, data: &T) -> Result<(), Value> {
67        let ret = unsafe {
68            (*self.n).ntuple_data = data as *const T as usize as *mut _;
69            (*self.n).size = std::mem::size_of::<T>() as _;
70            sys::gsl_ntuple_write(self.n)
71        };
72        result_handler!(ret, ())
73    }
74
75    /// This function is a synonym for NTuples::write.
76    #[doc(alias = "gsl_ntuple_bookdata")]
77    pub fn bookdata<T: Sized>(&mut self, data: &T) -> Result<(), Value> {
78        let ret = unsafe {
79            (*self.n).ntuple_data = data as *const T as usize as *mut _;
80            (*self.n).size = std::mem::size_of::<T>() as _;
81            sys::gsl_ntuple_bookdata(self.n)
82        };
83        result_handler!(ret, ())
84    }
85}
86
87impl Drop for WriteNTuples {
88    #[doc(alias = "gsl_ntuple_close")]
89    fn drop(&mut self) {
90        unsafe { sys::gsl_ntuple_close(self.n) };
91    }
92}
93
94pub struct ReadNTuples {
95    n: *mut sys::gsl_ntuple,
96}
97
98impl ReadNTuples {
99    /// This function opens an existing ntuple file filename for reading and returns a pointer to a
100    /// corresponding ntuple struct. The ntuples in the file must have size size. A pointer to
101    /// memory for the current ntuple row ntuple_data must be supplied—this is used to copy ntuples
102    /// in and out of the file.
103    #[doc(alias = "gsl_ntuple_open")]
104    pub fn open<P: AsRef<Path>>(filename: P) -> Option<ReadNTuples> {
105        let filename = filename.as_ref();
106        let filename = filename.to_str().expect("Failed to convert path to str");
107        let c_str = CString::new(filename.as_bytes()).unwrap();
108        let tmp =
109            unsafe { sys::gsl_ntuple_open(c_str.as_ptr() as *mut c_char, std::ptr::null_mut(), 0) };
110
111        if tmp.is_null() {
112            None
113        } else {
114            Some(Self { n: tmp })
115        }
116    }
117
118    /// This function reads the current row of the ntuple file for ntuple and stores the values in
119    /// ntuple->data.
120    #[doc(alias = "gsl_ntuple_read")]
121    pub fn read<T: Sized>(&mut self) -> Result<T, Value> {
122        let mut data = MaybeUninit::<T>::uninit();
123
124        let ret = unsafe {
125            (*self.n).ntuple_data = data.as_mut_ptr() as *mut _;
126            (*self.n).size = std::mem::size_of::<T>() as _;
127            sys::gsl_ntuple_read(self.n)
128        };
129        result_handler!(ret, unsafe { data.assume_init() })
130    }
131}
132
133impl Drop for ReadNTuples {
134    #[doc(alias = "gsl_ntuple_close")]
135    fn drop(&mut self) {
136        unsafe { sys::gsl_ntuple_close(self.n) };
137    }
138}
139
140macro_rules! impl_project {
141    ($name:ident) => {
142        impl $name {
143            /// This function updates the histogram `h` from the ntuple `ntuple` using the functions
144            /// `value_func` and `select_func`. For each ntuple row where the selection function
145            /// `select_func` is non-zero the corresponding value of that row is computed using the function
146            /// `value_func` and added to the histogram. Those ntuple rows where `select_func` returns
147            /// `false` are ignored. New entries are added to the histogram, so subsequent calls can be used
148            /// to accumulate further data in the same histogram.
149            #[doc(alias = "gsl_ntuple_project")]
150            pub fn project<T: Sized, V: Fn(&T) -> f64, S: Fn(&T) -> bool>(
151                &self,
152                h: &mut ::Histogram,
153                value_func: V,
154                select_func: S,
155            ) -> Result<(), Value> {
156                unsafe extern "C" fn value_trampoline<T: Sized, F: Fn(&T) -> f64>(
157                    x: *mut c_void,
158                    params: *mut c_void,
159                ) -> f64 {
160                    let f: &F = &*(params as *const F);
161                    let x: &T = &*(x as *const T);
162                    f(x)
163                }
164                unsafe extern "C" fn select_trampoline<T: Sized, F: Fn(&T) -> bool>(
165                    x: *mut c_void,
166                    params: *mut c_void,
167                ) -> i32 {
168                    let f: &F = &*(params as *const F);
169                    let x: &T = &*(x as *const T);
170                    if f(x) {
171                        1
172                    } else {
173                        0
174                    }
175                }
176
177                let f: Box<V> = Box::new(value_func);
178                let mut value_function = sys::gsl_ntuple_value_fn {
179                    function: unsafe { std::mem::transmute(value_trampoline::<T, V> as usize) },
180                    params: Box::into_raw(f) as *mut _,
181                };
182                let f: Box<S> = Box::new(select_func);
183                let mut select_function = sys::gsl_ntuple_select_fn {
184                    function: unsafe { std::mem::transmute(select_trampoline::<T, S> as usize) },
185                    params: Box::into_raw(f) as *mut _,
186                };
187                let ret = unsafe {
188                    sys::gsl_ntuple_project(
189                        h.unwrap_unique(),
190                        self.n,
191                        &mut value_function,
192                        &mut select_function,
193                    )
194                };
195                result_handler!(ret, ())
196            }
197        }
198    };
199}
200
201impl_project!(WriteNTuples);
202impl_project!(ReadNTuples);