ucndata 5.0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ucndata/__init__.py +55 -0
- ucndata/applylist.py +205 -0
- ucndata/chopper.py +718 -0
- ucndata/constants.py +7 -0
- ucndata/datetime.py +122 -0
- ucndata/exceptions.py +21 -0
- ucndata/tsubfile.py +80 -0
- ucndata/ttreeslow.py +261 -0
- ucndata/ucnbase.py +630 -0
- ucndata/ucncycle.py +556 -0
- ucndata/ucnperiod.py +228 -0
- ucndata/ucnrun.py +1104 -0
- ucndata/version.py +1 -0
- ucndata-5.0.5.dist-info/METADATA +65 -0
- ucndata-5.0.5.dist-info/RECORD +17 -0
- ucndata-5.0.5.dist-info/WHEEL +4 -0
- ucndata-5.0.5.dist-info/licenses/LICENSE +21 -0
ucndata/__init__.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# init
|
|
2
|
+
# Derek Fujimoto
|
|
3
|
+
# June 2024
|
|
4
|
+
|
|
5
|
+
from .ucnrun import ucnrun
|
|
6
|
+
from .ucncycle import ucncycle
|
|
7
|
+
from .ucnperiod import ucnperiod
|
|
8
|
+
from .ucnbase import ucnbase
|
|
9
|
+
from .ttreeslow import ttreeslow
|
|
10
|
+
from .applylist import applylist
|
|
11
|
+
from .exceptions import *
|
|
12
|
+
from .datetime import to_datetime, from_datetime
|
|
13
|
+
|
|
14
|
+
# path to the directory which contains the root files
|
|
15
|
+
DATADIR = "/data3/ucn/root_files"
|
|
16
|
+
|
|
17
|
+
# detector tree names
|
|
18
|
+
DET_NAMES = {'He3':{'hits': 'UCNHits_He3',
|
|
19
|
+
'hits_orig': 'UCNHits_He3', # as saved in the root file
|
|
20
|
+
'charge': 'He3_Charge',
|
|
21
|
+
'rate': 'He3_Rate',
|
|
22
|
+
'transitions': 'RunTransitions_He3',
|
|
23
|
+
'hitsseq': 'hitsinsequence_he3',
|
|
24
|
+
'hitsseqcumul': 'hitsinsequencecumul_he3',
|
|
25
|
+
},
|
|
26
|
+
'Li6':{'hits': 'UCNHits_Li6',
|
|
27
|
+
'hits_orig': 'UCNHits_Li-6', # as saved in the root file
|
|
28
|
+
'charge': 'Li6_Charge',
|
|
29
|
+
'rate': 'Li6_Rate',
|
|
30
|
+
'transitions': 'RunTransitions_Li6',
|
|
31
|
+
'hitsseq': 'hitsinsequence_li6',
|
|
32
|
+
'hitsseqcumul': 'hitsinsequencecumul_li6',
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# needed slow control trees: for checking data quality
|
|
37
|
+
SLOW_TREES = ('BeamlineEpics', 'SequencerTree')
|
|
38
|
+
|
|
39
|
+
# EPICS trees to group into a single slow control tree
|
|
40
|
+
EPICS_TREES = [ 'BeamlineEpics', 'UCN2EpPha5Last', 'UCN2EpicsPhase2B',
|
|
41
|
+
'UCN2EpPha5Oth', 'UCN2EpicsPhase3', 'UCN2EpPha5Pre',
|
|
42
|
+
'UCN2EpicsPressures', 'UCN2EpPha5Tmp', 'UCN2EpicsTemperature',
|
|
43
|
+
'UCN2Epics', 'UCN2Pur', 'UCN2EpicsOthers',
|
|
44
|
+
'UCN2EpLD2'
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
# data thresholds for checking data
|
|
48
|
+
DATA_CHECK_THRESH = {'beam_min_current': 0.1, # uA
|
|
49
|
+
'pileup_cnt_per_ms': 10, # if larger than this, then pileup and delete
|
|
50
|
+
'pileup_within_first_s': 1, # time frame for pileup in each period
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# precise cycle defaults
|
|
54
|
+
DEFAULT_CYCLE_TIMES_PRECISE = { 'hw_channel': 10,
|
|
55
|
+
'detector': 'Li6'}
|
ucndata/applylist.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Array which syntatic sugar to apply functions to each element contained
|
|
2
|
+
# Derek Fujimoto
|
|
3
|
+
# Oct 2024
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
class applylist(list):
|
|
8
|
+
"""A list object with the following enhancements:
|
|
9
|
+
|
|
10
|
+
* An `apply` function: like in pandas, `apply` takes a function handle and applies it to every element in the list. This acts recursively, if the list has a depth of more than 1
|
|
11
|
+
* Element access: accessing attributes, if not an attribute of the `applylist`, instead try to fetch attributes of the contained objects. The same for functions. This also works recursively.
|
|
12
|
+
* Numpy-like array slicing: slicing with a `np.ndarray` first converts this object to an array, then does the slice, then converts back. This allows slicing on arrays of indices for re-ordering or booleans for selection based on criteria
|
|
13
|
+
* Arithmetic works element-wise as in numpy arrays
|
|
14
|
+
|
|
15
|
+
Examples:
|
|
16
|
+
|
|
17
|
+
>>> # Slicing
|
|
18
|
+
>>> x = applylist(range(10))
|
|
19
|
+
>>> print(x[3:7])
|
|
20
|
+
[3, 4, 5, 6]
|
|
21
|
+
|
|
22
|
+
>>> # Element access
|
|
23
|
+
>>> x = applylist([ucnrun(1846), ucnrun(1847)])
|
|
24
|
+
>>> print(x.run_number)
|
|
25
|
+
[1846, 1847]
|
|
26
|
+
>>> print(x.beam_current_uA.mean())
|
|
27
|
+
[np.float64(0.16612837637441483), np.float64(0.18927602913972205)]
|
|
28
|
+
|
|
29
|
+
>>> # Arithmetic and comparisons
|
|
30
|
+
>>> x = applylist([1,2,3])
|
|
31
|
+
>>> print(x*2)
|
|
32
|
+
[np.int64(2), np.int64(4), np.int64(6)]
|
|
33
|
+
>>> print(x>2)
|
|
34
|
+
[np.False_, np.False_, np.True_]
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __call__(self, *args, **kwargs):
|
|
38
|
+
|
|
39
|
+
# call function of sub-elements
|
|
40
|
+
return applylist([x.__call__(*args, **kwargs) for x in self])
|
|
41
|
+
|
|
42
|
+
# numpy-style comparisons
|
|
43
|
+
def __eq__(self, other): return applylist(np.array(self).__eq__(other))
|
|
44
|
+
def __ne__(self, other): return applylist(np.array(self).__ne__(other))
|
|
45
|
+
def __lt__(self, other): return applylist(np.array(self).__lt__(other))
|
|
46
|
+
def __gt__(self, other): return applylist(np.array(self).__gt__(other))
|
|
47
|
+
def __le__(self, other): return applylist(np.array(self).__le__(other))
|
|
48
|
+
def __ge__(self, other): return applylist(np.array(self).__ge__(other))
|
|
49
|
+
|
|
50
|
+
# numpy-style unary operators
|
|
51
|
+
def __pos__(self): return applylist(np.array(self).__pos__())
|
|
52
|
+
def __neg__(self): return applylist(np.array(self).__neg__())
|
|
53
|
+
def __abs__(self): return applylist(np.array(self).__abs__())
|
|
54
|
+
def __invert__(self): return applylist(np.array(self).__invert__())
|
|
55
|
+
def __round__(self): return applylist(np.array(self).__round__())
|
|
56
|
+
def __floor__(self): return applylist(np.array(self).__floor__())
|
|
57
|
+
def __ceil__(self): return applylist(np.array(self).__ceil__())
|
|
58
|
+
def __trunc__(self): return applylist(np.array(self).__trunc__())
|
|
59
|
+
|
|
60
|
+
# numpy-style arithmetic operators
|
|
61
|
+
def __add__(self, other): return applylist(np.array(self).__add__(other))
|
|
62
|
+
def __sub__(self, other): return applylist(np.array(self).__sub__(other))
|
|
63
|
+
def __mul__(self, other): return applylist(np.array(self).__mul__(other))
|
|
64
|
+
def __floordiv__(self, other): return applylist(np.array(self).__floordiv__(other))
|
|
65
|
+
def __div__(self, other): return applylist(np.array(self).__div__(other))
|
|
66
|
+
def __truediv__(self, other): return applylist(np.array(self).__truediv__(other))
|
|
67
|
+
def __mod__(self, other): return applylist(np.array(self).__mod__(other))
|
|
68
|
+
def __divmod__(self, other): return applylist(np.array(self).__divmod__(other))
|
|
69
|
+
def __pow__(self, other): return applylist(np.array(self).__pow__(other))
|
|
70
|
+
def __and__(self, other): return applylist(np.array(self).__and__(other))
|
|
71
|
+
def __or__(self, other): return applylist(np.array(self).__or__(other))
|
|
72
|
+
def __xor__(self, other): return applylist(np.array(self).__xor__(other))
|
|
73
|
+
|
|
74
|
+
def __getattr__(self, name):
|
|
75
|
+
|
|
76
|
+
# retain default behaviour
|
|
77
|
+
try:
|
|
78
|
+
return super().__getattr__(name)
|
|
79
|
+
|
|
80
|
+
# enhanced behaviour
|
|
81
|
+
except AttributeError as err:
|
|
82
|
+
|
|
83
|
+
# check for scalar numpy datatypes, all contents assumed to be of the same type
|
|
84
|
+
if isinstance(self[0], (int, float, np.integer, np.floating)) or '__array_' in name:
|
|
85
|
+
raise err from None
|
|
86
|
+
|
|
87
|
+
# try to get the contents of subarray
|
|
88
|
+
return applylist([getattr(x, name) for x in self])
|
|
89
|
+
|
|
90
|
+
def __getitem__(self, key):
|
|
91
|
+
|
|
92
|
+
# numpy-like slicing
|
|
93
|
+
if isinstance(key, (np.ndarray, tuple, list, applylist)):
|
|
94
|
+
key = np.asarray(key)
|
|
95
|
+
|
|
96
|
+
# boolean array
|
|
97
|
+
if key.dtype == bool:
|
|
98
|
+
return applylist([i for i, k in zip(self, key) if k])
|
|
99
|
+
|
|
100
|
+
# index array
|
|
101
|
+
elif key.dtype == int:
|
|
102
|
+
return applylist([self[k] for k in key])
|
|
103
|
+
|
|
104
|
+
# default behaviour
|
|
105
|
+
else:
|
|
106
|
+
x = super().__getitem__(key)
|
|
107
|
+
if isinstance(x, list):
|
|
108
|
+
return applylist(x)
|
|
109
|
+
return x
|
|
110
|
+
|
|
111
|
+
def astype(self, typecast):
|
|
112
|
+
"""Convert all elements in-place to the given type.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
typecast (type): type to cast each element to (e.g. int, float, str)
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
None: modifies the list in-place
|
|
119
|
+
|
|
120
|
+
Example:
|
|
121
|
+
|
|
122
|
+
>>> x = applylist(np.arange(5))
|
|
123
|
+
>>> print(x)
|
|
124
|
+
[np.int64(0), np.int64(1), np.int64(2), np.int64(3), np.int64(4)]
|
|
125
|
+
>>> x.astype(int)
|
|
126
|
+
>>> print(x)
|
|
127
|
+
[0, 1, 2, 3, 4]
|
|
128
|
+
"""
|
|
129
|
+
for i in range(len(self)):
|
|
130
|
+
self[i] = typecast(self[i])
|
|
131
|
+
|
|
132
|
+
def apply(self, fn, inplace=False):
|
|
133
|
+
"""Apply function to each element contained, similar to pandas functionality
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
fn (function handle): function to apply to each element
|
|
137
|
+
inplace (bool): if `False` return a copy, else act in-place
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
applylist|None: new `applylist` with results if `inplace` is `False`, else `None`
|
|
141
|
+
|
|
142
|
+
Examples:
|
|
143
|
+
|
|
144
|
+
>>> # With return value
|
|
145
|
+
>>> x = applylist(np.arange(5))
|
|
146
|
+
>>> y = x.apply(lambda a: a**2)
|
|
147
|
+
>>> print(x)
|
|
148
|
+
[np.int64(0), np.int64(1), np.int64(2), np.int64(3), np.int64(4)]
|
|
149
|
+
>>> print(y)
|
|
150
|
+
[np.int64(0), np.int64(1), np.int64(4), np.int64(9), np.int64(16)]
|
|
151
|
+
|
|
152
|
+
>>> # Inplace
|
|
153
|
+
>>> x = applylist(np.arange(5))
|
|
154
|
+
>>> x.apply(lambda a: a**2, inplace=True)
|
|
155
|
+
>>> print(x)
|
|
156
|
+
[np.int64(0), np.int64(1), np.int64(4), np.int64(9), np.int64(16)]
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
# decide if inplace
|
|
160
|
+
if inplace: copy = self
|
|
161
|
+
else: copy = self.copy()
|
|
162
|
+
|
|
163
|
+
# do the operation
|
|
164
|
+
for i in range(len(copy)):
|
|
165
|
+
if isinstance(copy[i], applylist):
|
|
166
|
+
copy[i] = copy[i].apply(fn)
|
|
167
|
+
else:
|
|
168
|
+
copy[i] = fn(copy[i])
|
|
169
|
+
|
|
170
|
+
# return the copy
|
|
171
|
+
if not inplace: return copy
|
|
172
|
+
|
|
173
|
+
def copy(self):
|
|
174
|
+
"""Return a shallow copy of this applylist.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
applylist: a new `applylist` with the same elements
|
|
178
|
+
|
|
179
|
+
Example:
|
|
180
|
+
|
|
181
|
+
>>> x = applylist([1, 2, 3])
|
|
182
|
+
>>> y = x.copy()
|
|
183
|
+
>>> y[0] = 99
|
|
184
|
+
>>> print(x)
|
|
185
|
+
[1, 2, 3]
|
|
186
|
+
>>> print(y)
|
|
187
|
+
[99, 2, 3]
|
|
188
|
+
"""
|
|
189
|
+
return applylist(super().copy())
|
|
190
|
+
|
|
191
|
+
def transpose(self):
|
|
192
|
+
"""Transpose a 2D applylist by converting to np.array and back.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
applylist: transposed list, where rows and columns are swapped
|
|
196
|
+
|
|
197
|
+
Example:
|
|
198
|
+
|
|
199
|
+
>>> x = applylist([[1,2,3], [4,5,6]])
|
|
200
|
+
>>> print(x)
|
|
201
|
+
[[1, 2, 3], [4, 5, 6]]
|
|
202
|
+
>>> print(x.transpose())
|
|
203
|
+
[array([1, 4]), array([2, 5]), array([3, 6])]
|
|
204
|
+
"""
|
|
205
|
+
return applylist(np.array(self).transpose())
|