amor-eos 3.3.4__tar.gz → 3.3.5__tar.gz

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.
Files changed (42) hide show
  1. {amor_eos-3.3.4 → amor_eos-3.3.5}/PKG-INFO +1 -1
  2. {amor_eos-3.3.4 → amor_eos-3.3.5}/amor_eos.egg-info/PKG-INFO +1 -1
  3. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/__init__.py +2 -2
  4. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/event_analysis.py +11 -4
  5. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/event_data_types.py +1 -1
  6. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/event_handling.py +23 -16
  7. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/file_reader.py +18 -5
  8. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/options.py +10 -0
  9. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/projection.py +74 -3
  10. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/reduction_e2h.py +11 -4
  11. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/reduction_reflectivity.py +11 -8
  12. {amor_eos-3.3.4 → amor_eos-3.3.5}/tests/test_event_handling.py +50 -11
  13. {amor_eos-3.3.4 → amor_eos-3.3.5}/LICENSE +0 -0
  14. {amor_eos-3.3.4 → amor_eos-3.3.5}/README.rst +0 -0
  15. {amor_eos-3.3.4 → amor_eos-3.3.5}/amor_eos.egg-info/SOURCES.txt +0 -0
  16. {amor_eos-3.3.4 → amor_eos-3.3.5}/amor_eos.egg-info/dependency_links.txt +0 -0
  17. {amor_eos-3.3.4 → amor_eos-3.3.5}/amor_eos.egg-info/entry_points.txt +0 -0
  18. {amor_eos-3.3.4 → amor_eos-3.3.5}/amor_eos.egg-info/requires.txt +0 -0
  19. {amor_eos-3.3.4 → amor_eos-3.3.5}/amor_eos.egg-info/top_level.txt +0 -0
  20. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/__main__.py +0 -0
  21. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/command_line.py +0 -0
  22. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/compat.py +0 -0
  23. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/const.py +0 -0
  24. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/e2h.py +0 -0
  25. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/header.py +0 -0
  26. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/helpers.py +0 -0
  27. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/helpers_fallback.py +0 -0
  28. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/helpers_numba.py +0 -0
  29. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/instrument.py +0 -0
  30. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/kafka_events.py +0 -0
  31. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/kafka_serializer.py +0 -0
  32. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/logconfig.py +0 -0
  33. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/ls.py +0 -0
  34. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/nicos.py +0 -0
  35. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/nicos_interface.py +0 -0
  36. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/normalization.py +0 -0
  37. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/path_handling.py +0 -0
  38. {amor_eos-3.3.4 → amor_eos-3.3.5}/eos/reduction_kafka.py +0 -0
  39. {amor_eos-3.3.4 → amor_eos-3.3.5}/pyproject.toml +0 -0
  40. {amor_eos-3.3.4 → amor_eos-3.3.5}/setup.cfg +0 -0
  41. {amor_eos-3.3.4 → amor_eos-3.3.5}/tests/test_full_analysis.py +0 -0
  42. {amor_eos-3.3.4 → amor_eos-3.3.5}/tests/test_full_analysis_v2.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amor_eos
3
- Version: 3.3.4
3
+ Version: 3.3.5
4
4
  Summary: EOS reflectometry reduction for AMOR instrument
5
5
  Author: Jochen Stahn - Paul Scherrer Institut
6
6
  Author-email: jochen.stahn@psi.ch
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amor_eos
3
- Version: 3.3.4
3
+ Version: 3.3.5
4
4
  Summary: EOS reflectometry reduction for AMOR instrument
5
5
  Author: Jochen Stahn - Paul Scherrer Institut
6
6
  Author-email: jochen.stahn@psi.ch
@@ -2,5 +2,5 @@
2
2
  Package to handle data redction at the AMOR instrument to be used by __main__.py script.
3
3
  """
4
4
 
5
- __version__ = '3.3.4'
6
- __date__ = '2026-08-27'
5
+ __version__ = '3.3.5'
6
+ __date__ = '2026-11-06'
@@ -19,7 +19,7 @@ class ExtractWalltime(EventDataAction):
19
19
  wallTime = extract_walltime(dataset.data.events.tof,
20
20
  dataset.data.packets.start_index,
21
21
  dataset.data.packets.time)
22
- logging.debug(f' expending event stream by wallTime')
22
+ logging.debug(f' expending event stream by wallTime')
23
23
  new_events = append_fields(dataset.data.events, [('wallTime', wallTime.dtype)])
24
24
  new_events.wallTime = wallTime
25
25
  dataset.data.events = new_events
@@ -156,12 +156,14 @@ class FilterQzRange(EventDataAction):
156
156
 
157
157
  class FilterByLog(EventDataAction):
158
158
 
159
- def __init__(self, filter_string, remove_switchpulse=False):
159
+ def __init__(self, filter_string, remove_switchpulse=False, pre_switch=0, post_switch=0):
160
160
  if filter_string.startswith('!'):
161
161
  filter_string = filter_string[1:]
162
162
  remove_switchpulse = True
163
163
  self.filter_string = filter_string
164
164
  self.remove_switchpulse = remove_switchpulse
165
+ self.pre_switch = pre_switch
166
+ self.post_switch = post_switch
165
167
 
166
168
  def perform_action(self, dataset: EventDatasetProtocol) -> None:
167
169
  filter_variable = None
@@ -184,8 +186,13 @@ class FilterByLog(EventDataAction):
184
186
  add_log_to_pulses(filter_variable, dataset)
185
187
  fltr_pulses = eval(self.filter_string, {filter_variable: dataset.data.pulses[filter_variable]})
186
188
  if self.remove_switchpulse:
187
- switched = fltr_pulses[:-1] & ~fltr_pulses[1:]
188
- fltr_pulses[:-1] &= ~switched
189
+ switched = np.logical_xor(fltr_pulses[:-1], fltr_pulses[1:])
190
+ fltr_pulses[:-1] &= np.logical_not(switched)
191
+ for i in range(self.pre_switch):
192
+ fltr_pulses[:-2-i] &= np.logical_not(switched[1+i:])
193
+ for i in range(self.post_switch):
194
+ fltr_pulses[1+i:-1] &= np.logical_not(switched[:-1-i])
195
+ dataset.data.pulses.mask += EVENT_BITMASKS[filter_variable]*np.logical_not(fltr_pulses)
189
196
  goodTimeS = dataset.data.pulses.time[fltr_pulses]
190
197
  filter_e = np.logical_not(np.isin(dataset.data.events.wallTime, goodTimeS))
191
198
  dataset.data.events.mask += EVENT_BITMASKS[filter_variable]*filter_e
@@ -37,7 +37,7 @@ class DA00EventBuffer:
37
37
  # Structured datatypes used for event streams
38
38
  EVENT_TYPE = np.dtype([('tof', np.float64), ('pixelID', np.uint32), ('mask', np.int32)])
39
39
  PACKET_TYPE = np.dtype([('start_index', np.uint32), ('time', np.int64)])
40
- PULSE_TYPE = np.dtype([('time', np.int64), ('monitor', np.float32)])
40
+ PULSE_TYPE = np.dtype([('time', np.int64), ('monitor', np.float32), ('mask', np.int32)])
41
41
  PC_TYPE = np.dtype([('current', np.float32), ('time', np.int64)])
42
42
  BM_TYPE = np.dtype([('value', np.int32), ('time', np.int64)])
43
43
  LOG_TYPE = np.dtype([('value', np.float32), ('time', np.int64)])
@@ -134,6 +134,7 @@ class FilterMonitorThreshold(EventDataAction):
134
134
  raise ValueError(
135
135
  "FilterMonitorThreshold requires walltTime to be extracted, please run ExtractWalltime first")
136
136
  low_current_filter = dataset.data.pulses.monitor>2*dataset.timing.tau*self.lowCurrentThreshold*1e-3
137
+ dataset.data.pulses.mask[np.logical_not(low_current_filter)] = EVENT_BITMASKS['MonitorThreshold']
137
138
  dataset.data.pulses.monitor[np.logical_not(low_current_filter)] = 0.
138
139
  goodTimeS = dataset.data.pulses.time[low_current_filter]
139
140
  filter_e = np.logical_not(np.isin(dataset.data.events.wallTime, goodTimeS))
@@ -197,19 +198,25 @@ class ApplyMask(EventDataAction):
197
198
  logging.info(f' number of events: total = {pre_filter:7d}, after filtering = {post_filter:7d}')
198
199
  if d.device_logs == {} or not hasattr(dataset, 'update_info_from_logs'):
199
200
  return
200
- if 'wallTime' in d.events.dtype.names:
201
- # filter pulses and logs to allow update of header information
202
- from .helpers import add_log_to_pulses
203
- times = np.unique(d.events.wallTime)
204
- # make sure all log variables are associated with pulses
205
- for key, log in d.device_logs.items():
206
- if not key in d.pulses.dtype.names:
207
- # interpolate the parameter values for all existing pulses
208
- add_log_to_pulses(key, dataset)
209
- # remove all pulses that have no more events
210
- d.pulses = d.pulses[np.isin(d.pulses.time, times)]
211
- for key, log in d.device_logs.items():
212
- d.device_logs[key] = np.recarray(d.pulses.shape, dtype = log.dtype)
213
- d.device_logs[key].time = d.pulses.time
214
- d.device_logs[key].value = d.pulses[key]
215
- dataset.update_info_from_logs()
201
+ # filter pulses, not all masks apply to pulses
202
+ pre_filter_pulses = d.pulses.shape[0]
203
+ # make sure all log variables are associated with pulses
204
+ for key, log in d.device_logs.items():
205
+ if not key in d.pulses.dtype.names:
206
+ from .helpers import add_log_to_pulses
207
+ # interpolate the parameter values for all existing pulses
208
+ add_log_to_pulses(key, dataset)
209
+ if self.bitmask_filter is None:
210
+ d.pulses = d.pulses[d.pulses.mask==0]
211
+ else:
212
+ # remove the provided bitmask_filter bits from the events
213
+ # this means that all bits that are set in bitmask_filter will NOT be used to filter events
214
+ fltr = (d.pulses.mask & (~self.bitmask_filter)) == 0
215
+ d.pulses = d.pulses[fltr]
216
+ post_filter_pulses = d.pulses.shape[0]
217
+ logging.info(f' number of pulses: total = {pre_filter_pulses:7d}, after filtering = {post_filter_pulses:7d}')
218
+ for key, log in d.device_logs.items():
219
+ d.device_logs[key] = np.recarray(d.pulses.shape, dtype = log.dtype)
220
+ d.device_logs[key].time = d.pulses.time
221
+ d.device_logs[key].value = d.pulses[key]
222
+ dataset.update_info_from_logs()
@@ -527,11 +527,11 @@ class AmorEventData(AmorHeader):
527
527
  pulseTimeS = self._beam_monitor.time
528
528
  elif self.correctTimeFallback:
529
529
  logging.critical(
530
- ' No chopper trigger data or monitor time available, using event stearm and packet times instead!')
530
+ ' No chopper trigger data or monitor time available, using event stream and packet times instead!')
531
531
  data_group = self.hdf[self.hdf_paths['data'][0]]
532
532
  startTime = np.array(data_group['event_time_zero'][0], dtype=np.int64)
533
533
  stopTime = np.array(data_group['event_time_zero'][-1], dtype=np.int64)
534
- pulseTimeS = np.arange(startTime, stopTime, self.timing.tau*1e9, dtype=np.int64)
534
+ pulseTimeS = np.arange(startTime, stopTime+self.timing.tau*1e9, self.timing.tau*1e9, dtype=np.int64)
535
535
  # find times that are closest to the next packet time and correct them to fit exactly
536
536
  all_times = np.unique(packets.time)
537
537
  all_times.sort()
@@ -549,6 +549,7 @@ class AmorEventData(AmorHeader):
549
549
  pulses = np.recarray(pulseTimeS.shape, dtype=PULSE_TYPE)
550
550
  pulses.time = pulseTimeS
551
551
  pulses.monitor = 1. # default is monitor pulses as it requires no calculation
552
+ pulses.mask = 0
552
553
  # apply filter in case the events were filtered
553
554
  if (self.first_index>0 or not self.EOF):
554
555
  pulses = pulses[(pulses.time>=packets.time[0])&(pulses.time<=packets.time[-1])]
@@ -604,8 +605,9 @@ class AmorEventData(AmorHeader):
604
605
  output = np.recarray(a1.shape[0]+a2.shape[0], dtype=new_fields)
605
606
  for field in new_fields.fields.keys():
606
607
  output[field] = np.concatenate([a1[field], a2[field]])
607
- logging.warning("Missing data when combining datasets, "
608
- f"{tuple(a1.dtype.fields.keys())} != {tuple(a2.dtype.fields.keys())}")
608
+ logging.warning(" Missing data when combining datasets,\n"
609
+ f" {tuple(a1.dtype.fields.keys())} \n"
610
+ f" !={tuple(a2.dtype.fields.keys())}")
609
611
  return output
610
612
 
611
613
  def append(self, other):
@@ -625,7 +627,15 @@ class AmorEventData(AmorHeader):
625
627
  else:
626
628
  new_device_logs = {}
627
629
  for key, slog in self.data.device_logs.items():
628
- olog = other.data.device_logs[key]
630
+ try:
631
+ olog = other.data.device_logs[key]
632
+ except KeyError:
633
+ if getattr(other.experiment, 'end_date', None) is None:
634
+ logging.warning(f" Device logs for '{key}' not found in dataset to be added, "
635
+ f"ignoring key as file is not finalized")
636
+ continue
637
+ else:
638
+ raise KeyError(f"Device logs for '{key}' not found in dataset to be added")
629
639
  new_device_logs[key] = np.recarray(slog.shape[0]+olog.shape[0],
630
640
  dtype = slog.dtype)
631
641
  new_device_logs[key].time = np.concatenate([slog.time, olog.time])
@@ -664,4 +674,7 @@ class AmorEventData(AmorHeader):
664
674
  output.data = AmorEventStream(self.data.events[event_filter], self.data.packets,
665
675
  self.data.pulses[pulse_filter], self.data.proton_current,
666
676
  self.data.beam_monitor)
677
+ for key, value in self.data.device_logs.items():
678
+ output.data.device_logs[key] = value[pulse_filter]
679
+ output.update_info_from_logs()
667
680
  return output
@@ -499,6 +499,15 @@ class ReflectivityReductionConfig(ArgParsable):
499
499
  },
500
500
  )
501
501
 
502
+ flipperMaxDelay: int = field(
503
+ default=4,
504
+ metadata={
505
+ 'short': 'fmd',
506
+ 'group': 'data manicure',
507
+ 'help': 'Remove all neutron pulses that are within flipperMaxDelay of a change in spin state',
508
+ },
509
+ )
510
+
502
511
  class OutputFomatOption(StrEnum):
503
512
  Rqz_ort = "Rqz.ort"
504
513
  Rqz_orb = "Rqz.orb"
@@ -640,6 +649,7 @@ class E2HPlotSelection(StrEnum):
640
649
  YZ = 'Iyz'
641
650
  LT = 'Ilt'
642
651
  YT = 'Iyt'
652
+ Z = 'Iz'
643
653
  TZ = 'Itz'
644
654
  Q = 'Iq'
645
655
  L = 'Il'
@@ -237,7 +237,7 @@ class LZProjection(ProjectionInterface):
237
237
 
238
238
  def normalize_no_footprint(self, norm: LZNormalisation):
239
239
  norm_lz = norm.norm
240
- ref_lz = (self.data.I/norm_lz)
240
+ ref_lz = (self.data.I/np.where(norm_lz>0, norm_lz, np.nan))
241
241
  ref_lz *= norm.monitor/self.monitor
242
242
  ref_lz[np.logical_not(self.data.mask)] = np.nan
243
243
  self.data.norm = norm_lz
@@ -249,8 +249,8 @@ class LZProjection(ProjectionInterface):
249
249
  def scale(self, factor: float):
250
250
  if not self.is_normalized:
251
251
  raise ValueError("Dataset needs to be normalized, first")
252
- self.data.ref /= factor
253
- self.data.err /= factor
252
+ self.data.ref *= factor
253
+ self.data.err *= factor
254
254
  self.norm_monitor *= factor
255
255
 
256
256
  def project_on_qz(self):
@@ -636,6 +636,77 @@ class TofZProjection(ProjectionInterface):
636
636
  art.remove()
637
637
  plt.draw()
638
638
 
639
+ class FltrZProjection(ProjectionInterface):
640
+ z: np.ndarray
641
+ tau: float
642
+ foldback: bool
643
+ l_range: Tuple[float, float]
644
+
645
+ data: np.recarray
646
+ _dtype = np.dtype([
647
+ ('cts', np.float64),
648
+ ('I', np.float64),
649
+ ('err', np.float64),
650
+ ])
651
+
652
+ def __init__(self, tau, foldback=False, l_range=(0., 15.0)):
653
+ self.l_range = l_range
654
+ self.tau = tau
655
+ self.z = np.arange(Detector.nBlades*Detector.nWires+1)-0.5
656
+ self.foldback = foldback
657
+ self.data = np.zeros(self.z.shape[0]-1, dtype=self._dtype).view(np.recarray)
658
+ self.monitor = 0.
659
+
660
+ def project(self, dataset: EventDatasetProtocol, monitor: float):
661
+ detYi, detZi, detX, delta = Detector.pixelLookUp[dataset.data.events.pixelID-1].T
662
+
663
+ fltr = dataset.data.events.lamda>=self.l_range[0]
664
+ fltr &= dataset.data.events.lamda<=self.l_range[1]
665
+
666
+ cts , *_ = np.histogram(detZi[fltr], bins=self.z)
667
+ self.data.cts += cts
668
+ self.monitor += monitor
669
+
670
+ self.data.I = self.data.cts / self.monitor
671
+ self.data.err = np.sqrt(self.data.cts) / self.monitor
672
+
673
+ def clear(self):
674
+ self.data[:] = 0
675
+ self.monitor = 0.
676
+
677
+ def plot(self, **kwargs):
678
+ from matplotlib import pyplot as plt
679
+
680
+ Z = (self.z[1:]+self.z[:-1])/2.
681
+ self._graph = plt.errorbar(Z, self.data.I, yerr=self.data.err, **kwargs)
682
+ ez, eI = self.find_lower_edge()
683
+ plt.plot([ez], [eI], 'ro')
684
+ plt.text(ez, 0., f'{ez:.1f}', ha='center', va='bottom')
685
+
686
+ plt.xlabel('Z')
687
+ plt.xlim(self.z[-1], self.z[0])
688
+ plt.title('Vertical Pixel w/ filter')
689
+
690
+ def update_plot(self):
691
+ """
692
+ Inline update of previous plot by just updating the data.
693
+ """
694
+ from matplotlib.colors import LogNorm
695
+ self._graph.set_array(self.data.I)
696
+
697
+ def find_lower_edge(self):
698
+ Imax = self.data.I.max()
699
+ next_pix = np.where(self.data.I>Imax/2.)[0][-1]
700
+ I_next = self.data.I[next_pix]
701
+ I_prev = self.data.I[next_pix+1]
702
+
703
+ Z = (self.z[1:]+self.z[:-1])/2.
704
+ nxt_prev_ratio = (Imax/2.-I_prev)/(I_next - Imax/2.)
705
+ z = Z[next_pix+1] + nxt_prev_ratio*(Z[next_pix]-Z[next_pix+1])
706
+ print(I_next - Imax/2., Imax/2.-I_prev, )
707
+ return z, Imax/2.
708
+
709
+
639
710
  class TofProjection(ProjectionInterface):
640
711
  tof: np.ndarray
641
712
 
@@ -19,10 +19,11 @@ from .normalization import LZNormalisation
19
19
  from .options import E2HConfig, E2HPlotArguments, IncidentAngle, MonitorType, E2HPlotSelection
20
20
  from . import event_handling as eh
21
21
  from .path_handling import PathResolver
22
- from .projection import CombinedProjection, LProjection, LZProjection, ProjectionInterface, ReflectivityProjector, \
23
- TofProjection, TofZProjection, TProjection, YTProjection, YZProjection
22
+ from .projection import CombinedProjection, FltrZProjection, LProjection, LZProjection, ProjectionInterface, \
23
+ ReflectivityProjector, \
24
+ TofProjection, TofZProjection, TProjection, YTProjection, YZProjection
24
25
 
25
- NEEDS_LAMDA = (E2HPlotSelection.All, E2HPlotSelection.LT, E2HPlotSelection.Q, E2HPlotSelection.L)
26
+ NEEDS_LAMDA = (E2HPlotSelection.All, E2HPlotSelection.LT, E2HPlotSelection.Q, E2HPlotSelection.L, E2HPlotSelection.Z)
26
27
 
27
28
  class E2HReduction:
28
29
  config: E2HConfig
@@ -97,7 +98,8 @@ class E2HReduction:
97
98
  self.event_actions |= eh.ApplyMask()
98
99
 
99
100
  # plot dependant options
100
- if self.config.reduction.plot in [E2HPlotSelection.All, E2HPlotSelection.LT, E2HPlotSelection.Q]:
101
+ if self.config.reduction.plot in [E2HPlotSelection.All, E2HPlotSelection.LT,
102
+ E2HPlotSelection.Q, E2HPlotSelection.Z]:
101
103
  self.grid = LZGrid(0.05, [0.0, 0.25], lambda_overwrite=self.config.experiment.lambdaRange)
102
104
  self.grid.dldl = 0.01
103
105
 
@@ -195,6 +197,11 @@ class E2HReduction:
195
197
  if self.config.reduction.plot==E2HPlotSelection.TZ:
196
198
  self.projection = TofZProjection(last_file_header.timing.tau, foldback=not self.config.reduction.fast)
197
199
 
200
+ if self.config.reduction.plot==E2HPlotSelection.Z:
201
+ self.projection = FltrZProjection(last_file_header.timing.tau,
202
+ foldback=not self.config.reduction.fast,
203
+ l_range=self.config.experiment.lambdaRange)
204
+
198
205
  if self.config.reduction.plot==E2HPlotSelection.ToF:
199
206
  self.projection = TofProjection(last_file_header.timing.tau, foldback=not self.config.reduction.fast)
200
207
 
@@ -125,6 +125,8 @@ class ReflectivityReduction:
125
125
  _sampleName = 'unknown'
126
126
  _mu = int(self.dataset.geometry.mu * 3)
127
127
  self.config.output.outputName = f'{_sampleName}_{_mu:03}_{_date}'
128
+ elif self.config.output.outputName.endswith('.Rqz.ort'):
129
+ self.config.output.outputName = self.config.output.outputName[:-len('.Rqz.ort')]
128
130
 
129
131
  logging.warning('output:')
130
132
 
@@ -145,9 +147,10 @@ class ReflectivityReduction:
145
147
  logging.warning('input:')
146
148
  file_list = self.path_resolver.resolve(short_notation)
147
149
 
148
- self.header.measurement_data_files = []
149
150
 
150
151
  self.dataset = AmorEventData(file_list[0])
152
+ self.header.measurement_data_files = [
153
+ fileio.File( file=os.path.basename(file_list[0]), timestamp=self.dataset.fileDate)]
151
154
  if self.config.experiment.monitorType==MonitorType.auto:
152
155
  if self.dataset.data.proton_current.current.sum()>1:
153
156
  self.config.experiment.monitorType = MonitorType.proton_charge
@@ -169,10 +172,8 @@ class ReflectivityReduction:
169
172
  di = AmorEventData(fi)
170
173
  self.dataevent_actions(di)
171
174
  self.dataset.append(di)
172
-
173
- for fileName in file_list:
174
- self.header.measurement_data_files.append(fileio.File( file=os.path.basename(fileName),
175
- timestamp=self.dataset.fileDate))
175
+ self.header.measurement_data_files.append(fileio.File( file=os.path.basename(fi),
176
+ timestamp=di.fileDate))
176
177
 
177
178
  if 'polarization_config_label' in self.dataset.data.device_logs:
178
179
  pols = np.unique(self.dataset.data.device_logs['polarization_config_label'].value)
@@ -185,7 +186,9 @@ class ReflectivityReduction:
185
186
  for pi in pols:
186
187
  plabel = const.polarizationLabels[pi]
187
188
  pol_filter = FilterByLog(f'polarization_config_label=={pi}',
188
- remove_switchpulse=True) | ApplyMask()
189
+ remove_switchpulse=True,
190
+ pre_switch=self.config.reduction.flipperMaxDelay
191
+ ) | ApplyMask()
189
192
  logging.info(f' filter {plabel} using polarization_config_label=={pi}')
190
193
  pol_filter(self.dataset)
191
194
  self.dataset.update_header(self.header)
@@ -238,7 +241,7 @@ class ReflectivityReduction:
238
241
  logging.warning(
239
242
  f'backgroung file {self.sFileName} not compatible with q_z scale ({len(self.sq_q)} vs. {len(result.Q)})')
240
243
 
241
- if not self.config.reduction.is_default('scale'):
244
+ if self.config.reduction.autoscale and not self.config.reduction.is_default('scale'):
242
245
  try:
243
246
  scale = self.config.reduction.scale[i]
244
247
  except IndexError:
@@ -366,7 +369,7 @@ class ReflectivityReduction:
366
369
  self.last_result = result
367
370
  # reset normal logging behavior
368
371
  #logging.StreamHandler.terminator = "\n"
369
- logging.info(f' done {min(time+interval, pulseTimeS[-1]):5.0f}')
372
+ logging.info(f' done {min(time+interval, pulseTimeS[-1]):5.0f}')
370
373
 
371
374
  def save_Rqz(self):
372
375
  fname = os.path.join(self.config.output.outputPath, f'{self.config.output.outputName}.Rqz.ort')
@@ -53,6 +53,7 @@ class MockEventData:
53
53
  pulses = np.recarray((packets.shape[0],), dtype=PULSE_TYPE)
54
54
  pulses.monitor = 1.0
55
55
  pulses.time = packets.time
56
+ pulses.mask = 0
56
57
 
57
58
  # proton current information with independent timing
58
59
  proton_current = np.recarray((50,), dtype=PC_TYPE)
@@ -552,27 +553,65 @@ class TestSimpleActions(TestCase):
552
553
 
553
554
  self._extract_walltime()
554
555
 
555
- test_log = np.recarray(shape=(2,), dtype=np.dtype([('value', np.int32),
556
- ('time', np.int64)]))
557
- test_log.time = [-5, self.d.data.pulses.time[100]+123]
558
- test_log.value = [0, 1]
556
+ test_log = np.recarray(shape=(self.d.data.pulses.shape[0],),
557
+ dtype=np.dtype([('value', np.int32), ('time', np.int64)]))
558
+ test_log.time = self.d.data.pulses.time
559
+ test_log.value = 0
560
+ test_log.value[10:-10] = 1
559
561
  self.d.data.device_logs['test_log'] = test_log
560
562
  action.perform_action(self.d)
561
- self.assertEqual(self.d.data.pulses.shape[0], 101)
563
+ self.assertEqual(20, self.d.data.pulses.shape[0])
562
564
 
563
565
  def test_filter_by_log_switchpulse(self):
564
566
  action = FilterByLog("!test_log==0") | ApplyMask()
565
567
  self._extract_walltime()
566
568
 
567
- test_log = np.recarray(shape=(2,), dtype=np.dtype([('value', np.int32),
568
- ('time', np.int64)]))
569
- test_log.time = [-5, self.d.data.pulses.time[100]+123]
570
- test_log.value = [0, 1]
569
+ test_log = np.recarray(shape=(self.d.data.pulses.shape[0],),
570
+ dtype=np.dtype([('value', np.int32), ('time', np.int64)]))
571
+ test_log.time = self.d.data.pulses.time
572
+ test_log.value = 0
573
+ test_log.value[10:-10] = 1
571
574
  self.d.data.device_logs['test_log'] = test_log
572
575
  self.d.data.device_logs['check_log'] = test_log.copy()
573
576
  action.perform_action(self.d)
574
- self.assertEqual(self.d.data.pulses.shape[0], 100)
577
+ self.assertEqual(19, self.d.data.pulses.shape[0])
575
578
  np.testing.assert_array_equal(
576
579
  self.d.data.device_logs['test_log'],
577
580
  self.d.data.device_logs['check_log'],
578
- )
581
+ )
582
+
583
+ def test_filter_by_log_preswitch(self):
584
+ action = FilterByLog("!test_log==0", pre_switch=2) | ApplyMask()
585
+ self._extract_walltime()
586
+
587
+ test_log = np.recarray(shape=(self.d.data.pulses.shape[0],),
588
+ dtype=np.dtype([('value', np.int32), ('time', np.int64)]))
589
+ test_log.time = self.d.data.pulses.time
590
+ test_log.value = 0
591
+ test_log.value[10:-10] = 1
592
+ self.d.data.device_logs['test_log'] = test_log
593
+ self.d.data.device_logs['check_log'] = test_log.copy()
594
+ action.perform_action(self.d)
595
+ self.assertEqual(17, self.d.data.pulses.shape[0])
596
+ np.testing.assert_array_equal(
597
+ self.d.data.device_logs['test_log'],
598
+ self.d.data.device_logs['check_log'],
599
+ )
600
+
601
+ def test_filter_by_log_postswitch(self):
602
+ action = FilterByLog("!test_log==0", post_switch=2) | ApplyMask()
603
+ self._extract_walltime()
604
+
605
+ test_log = np.recarray(shape=(self.d.data.pulses.shape[0],),
606
+ dtype=np.dtype([('value', np.int32), ('time', np.int64)]))
607
+ test_log.time = self.d.data.pulses.time
608
+ test_log.value = 0
609
+ test_log.value[10:-10] = 1
610
+ self.d.data.device_logs['test_log'] = test_log
611
+ self.d.data.device_logs['check_log'] = test_log.copy()
612
+ action.perform_action(self.d)
613
+ self.assertEqual(17, self.d.data.pulses.shape[0])
614
+ np.testing.assert_array_equal(
615
+ self.d.data.device_logs['test_log'],
616
+ self.d.data.device_logs['check_log'],
617
+ )
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes