Skip to content

Commit c4224da

Browse files
committed
Add XtdfPulses
1 parent f57e71c commit c4224da

3 files changed

Lines changed: 92 additions & 1 deletion

File tree

docs/components/pulse-patterns.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@
66

77
::: extra.components.MachinePulses
88

9+
::: extra.components.XtdfPulses
10+
911
::: extra.components.DldPulses

src/extra/components/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11

22
from .scantool import Scantool # noqa
33
from .pulses import XrayPulses, OpticalLaserPulses, MachinePulses, \
4-
PumpProbePulses, DldPulses # noqa
4+
PumpProbePulses, XtdfPulses, AgipdPulses, LpdPulses, DsscPulses, \
5+
DldPulses # noqa
56
from .scan import Scan # noqa
67
from .xgm import XGM # noqa
78
from .dld import DelayLineDetector # noqa

src/extra/components/pulses.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1508,6 +1508,94 @@ def pulse_mask(self, labelled=True, field=None):
15081508
raise ValueError(f"{field=!r} parameter was not 'fel'/'ppl'/None")
15091509

15101510

1511+
class XtdfPulses(PulsePattern):
1512+
"""An interface to pulses from XTDF sources.
1513+
1514+
The custom fast MHz detectors used at European XFEL send their data
1515+
via the XFEL Train Data Format (XTDF), which contains information
1516+
about the pulse pattern recorded by these detectors.
1517+
1518+
This component exposes this information via the pulse pattern
1519+
interface. As XTDF detectors generally enumerate pulses
1520+
independently, it also allows to correlate its pulse ID with those
1521+
obtained from other global sources, e.g. the machine information
1522+
from [XrayPulses][extra.components.Xraypulses].
1523+
1524+
Args:
1525+
detector (SourceData): Instrument source of an XTDF source.
1526+
ppt_anchor (int or PulsePattern, optional): Constant offset to
1527+
add to XTDF pulse IDs, or PulsePattern-based component to
1528+
correlate every train individually. No correlation is done
1529+
by default.
1530+
first_lit_cell (int, optional): Which cell coincides with the
1531+
first pulse from passed ppt_anchor, ignored unless a
1532+
PulsePattern component is passed.
1533+
include_cells (bool, optional): Whether to include cell IDs
1534+
in the pulse index generated by this component, True by
1535+
default.
1536+
"""
1537+
1538+
def __init__(self, xtdf_source, ppt_anchor=None, first_lit_cell=0,
1539+
include_cells=True):
1540+
super().__init__(xtdf_source, xtdf_source['image.pulseId'])
1541+
1542+
self._ppt_anchor = ppt_anchor
1543+
self._first_lit_cell = first_lit_cell
1544+
self._cell_key = xtdf_source['image.cellId'] if include_cells else None
1545+
1546+
def __repr__(self):
1547+
return "<{} using {}>".format(type(self).__name__, self._source.source)
1548+
1549+
def _get_train_ids(self):
1550+
return self._source.train_ids
1551+
1552+
def _get_pulse_ids(self):
1553+
pulse_ids = self._key.ndarray().astype(np.int32).ravel()
1554+
1555+
if isinstance(self._ppt_anchor, int):
1556+
pulse_ids += self._ppt_anchor
1557+
1558+
elif isinstance(self._ppt_anchor, PulsePattern):
1559+
first, last = self._key.train_index_bounds(False)
1560+
1561+
# TODO: Must be aligned!
1562+
for i, (train_id, anchor_pids) in enumerate(
1563+
self._ppt_anchor.pulse_ids().groupby('trainId')
1564+
):
1565+
pulse_ids[first[i]:last[i]] += pids.iloc[0] - pulse_ids[
1566+
first[i] + self._first_lit_cell]
1567+
1568+
elif self._ppt_anchor is not None:
1569+
raise TypeError(type(self._ppt_anchor))
1570+
1571+
index_levels = {
1572+
'trainId': self._key.train_id_coordinates(),
1573+
'pulseIndex': np.concatenate([
1574+
np.arange(count, dtype=np.int32) for count
1575+
in self._key.data_counts(labelled=False)])}
1576+
1577+
if self._cell_key is not None:
1578+
index_levels['cellId'] = self._cell_key.ndarray().ravel()
1579+
1580+
import pandas as pd
1581+
index = pd.MultiIndex.from_arrays(
1582+
list(index_levels.values()), names=list(index_levels.keys()))
1583+
1584+
return pd.Series(data=pulse_ids, index=index, dtype=np.int32)
1585+
1586+
1587+
class AgipdPulses(XtdfPulses):
1588+
pass
1589+
1590+
1591+
class LpdPulses(XtdfPulses):
1592+
pass
1593+
1594+
1595+
class DsscPulses(XtdfPulses):
1596+
pass
1597+
1598+
15111599
class DldPulses(PulsePattern):
15121600
"""An interface to pulses from DLD reconstruction.
15131601

0 commit comments

Comments
 (0)