@@ -15,21 +15,57 @@ Header: !record
1515# types of timeBlocks
1616TimeBlock : [EventTimeBlock, ExternalSignalTimeBlock, BedMovementTimeBlock, GantryMovementTimeBlock, DeadTimeTimeBlock, SinglesHistogramTimeBlock]
1717
18+ # Type definition for a list of single events (for a module of a specific type)
19+ ListOfSingleEvents : SingleEvent*
20+
1821# Type definition for a list of coincidences, where all coincidences are between 2 modules of specific types
1922ListOfCoincidenceEvents : CoincidenceEvent*
2023
2124# Type definition for a list of triples, where all triples are between 3 modules of specific types
2225ListOfTripleEvents : TripleEvent*
2326
27+ # Type definition for a list of triples, where all triples are between 3 modules of specific types
28+ ListOfQuadrupleEvents : TripleEvent*
29+
30+ # time-block that stores the detected (gamma photon) events as lists.
31+ # An event time block optionally stores
32+ # - singles
33+ # - "prompt" coincidences (i.e. 2 photons detected within the coincidence window),
34+ # - "delayed"coincidences (i.e. 2 photons with arrival time within the coincidence window after subtracting a
35+ # (scanner dependent) offset for the second event)
36+ # - triples
37+ # - quadruples
38+ # Note: PETSIRD currently does not allow storing higher order coincidences. These are very unlikely to occur in practice.
39+ # It is expected that most scanners will only store prompt and delayed coincidences. If singles are stored, it is expected
40+ # that coincidences are not stored, but this is not guaranteed.
41+ #
42+ # See also the "policy" fields in ScannerInformation.
43+ # For instance, if coincidences, triples and quadruples are stored, it is likely that ScannerInformation.promptEventPolicy = rejectHigherMultiples,
44+ # ScannerInformation.tripleEventPolicy = rejectHigherMultiples, and ScannerInformation.quadrupleEventPolicy = rejectHigherMultiples as well,
45+ # but this is not guaranteed.
2446EventTimeBlock : !record
2547 fields :
2648 # time interval for this time block
2749 # We suggest that for each coincidence/multiple event, the earliest time of arrival
2850 # of a photon is used for deciding which time block the event is entered in.
2951 # However, the impact of this choice is negligible and therefore can be vendor dependent.
52+ # WARNING: if singleEvents are recorded, the time interval currently has to be smaller
53+ # than 2^32 picoseconds due to how time is stored, see SingleEvent.
3054 timeInterval : TimeInterval
31- # Nested vector with lists of prompts in this time block
32- # There is one separate list for every pair of module types.
55+
56+ # nested vector with lists of singles
57+ # If ScannerInformation.singleEventsAreStored is False, the vector should have
58+ # size 0. Otherwise, there is one separate list for every module type.
59+ # To get the list of singles for a type of module where the `detectionBin`
60+ # is in a module of the first type, use
61+ # singleEvents[typeOfModule]
62+ # Constraint: (size(singleEvents) > 0) == ScannerInformation.singleEventsAreStored
63+ # Constraint: (size(singleEvents) == 0) || (size(singleEvents) == ScannerInformation.numberOfModuleTypes)
64+ singleEvents : ListOfSingleEvents*
65+
66+ # nested vector with lists of prompts in this time block
67+ # If ScannerInformation.promptEventsAreStored is False, the vector should have
68+ # size 0. If not, there is one separate list for every pair of module types.
3369 # To get the list of prompts for 2 types of modules where the first `detectionBin`
3470 # given in the `CoincidenceEvent` is in a module of the first type, use
3571 # promptEvents[typeOfModule1][typeOfModule2]
@@ -38,24 +74,40 @@ EventTimeBlock: !record
3874 # but this is currently not enforced. Therefore, to check all coincidences between two
3975 # different types of modules, a user will have to check two lists, i.e. also
4076 # promptEvents[typeOfModule2][typeOfModule1]
41- # Constraint: size(promptsEvents) == ScannerInformation.numberOfModuleTypes
42- # Constraint: size(promptsEvents[*]) == ScannerInformation.numberOfModuleTypes
77+ # Constraint: (size(promptEvents) > 0) == ScannerInformation.promptEventsAreStored
78+ # Constraint: (size(promptEvents) == 0) || (size(promptsEvents) == ScannerInformation.numberOfModuleTypes)
79+ # Constraint: (size(promptEvents) == 0) || (size(promptsEvents[*]) == ScannerInformation.numberOfModuleTypes)
4380 promptEvents : ListOfCoincidenceEvents**
44- # optional nested vector with lists of delayed coincidences in this time block
81+
82+ # nested vector with lists of delayed coincidences in this time block
4583 # To get the list of delayeds for 2 types of modules, use
4684 # delayedEvents[typeOfModule1][typeOfModule2]
4785 # See promptEvents for more information.
48- # Constraint: size(delayedEvents) == ScannerInformation.numberOfModuleTypes
49- # Constraint: size(delayedEvents[*]) == ScannerInformation.numberOfModuleTypes
50- delayedEvents : ListOfCoincidenceEvents**?
51- # optional nested vector with lists of triple coincidences in this time block
86+ # Constraint: (size(delayedEvents) > 0) == ScannerInformation.delayedCoincidencesAreStored
87+ # Constraint: (size(delayedEvents) == 0) || (size(delayedEvents) == ScannerInformation.numberOfModuleTypes)
88+ # Constraint: (size(delayedEvents) == 0) || (size(delayedEvents[*]) == ScannerInformation.numberOfModuleTypes)
89+ delayedEvents : ListOfCoincidenceEvents**
90+
91+ # nested vector with lists of triple coincidences in this time block
5292 # To get the list of triples for 3 types of modules, use
5393 # tripleEvents[typeOfModule1][typeOfModule2][typeOfModule3]
5494 # See promptEvents for more information.
55- # Constraint: size(tripleEvents) == ScannerInformation.numberOfModuleTypes
56- # Constraint: size(tripleEvents[*]) == ScannerInformation.numberOfModuleTypes
57- # Constraint: size(tripleEvents[*][*]) == ScannerInformation.numberOfModuleTypes
58- tripleEvents : ListOfTripleEvents***?
95+ # Constraint: (size(tripleEvents) > 0) == ScannerInformation.tripleEventsAreStored
96+ # Constraint: (size(tripleEvents) == 0) || (size(tripleEvents) == ScannerInformation.numberOfModuleTypes)
97+ # Constraint: (size(tripleEvents) == 0) || (size(tripleEvents[*]) == ScannerInformation.numberOfModuleTypes)
98+ # Constraint: (size(tripleEvents) == 0) || (size(tripleEvents[*][*]) == ScannerInformation.numberOfModuleTypes)
99+ tripleEvents : ListOfTripleEvents***
100+
101+ # nested vector with lists of quadruple coincidences in this time block
102+ # To get the list of quadruples for 4 types of modules, use
103+ # quadrupleEvents[typeOfModule1][typeOfModule2][typeOfModule3][typeOfModule4]
104+ # See promptEvents for more information.
105+ # Constraint: (size(quadrupleEvents) > 0) == ScannerInformation.quadrupleEventsAreStored
106+ # Constraint: (size(quadrupleEvents) == 0) || (size(quadrupleEvents) == ScannerInformation.numberOfModuleTypes)
107+ # Constraint: (size(quadrupleEvents) == 0) || (size(quadrupleEvents[*]) == ScannerInformation.numberOfModuleTypes)
108+ # Constraint: (size(quadrupleEvents) == 0) || (size(quadrupleEvents[*][*]) == ScannerInformation.numberOfModuleTypes)
109+ # Constraint: (size(quadrupleEvents) == 0) || (size(quadrupleEvents[*][*][*]) == ScannerInformation.numberOfModuleTypes)
110+ quadrupleEvents : ListOfQuadrupleEvents****
59111
60112ExternalSignalTypeEnum : !enum
61113 values :
@@ -116,7 +168,12 @@ DeadTimeTimeBlock: !record
116168SinglesHistogram : uint64[singlesDetectionBin]
117169
118170# A time block that stores a singles histogram.
119- # See SinglesHistogramLevelType for information.
171+ # Note that all detected singles (within the ScannerInformation.singlesHistogramEnergyBinEdges)
172+ # should be stored. This is in contrast to the EventTimeBlock.singleEvents, where optionally
173+ # only singles that are in coincidence are recorded (see SingleEventPolicy).
174+ #
175+ # See also SinglesHistogramLevelType for information.
176+ #
120177# Constraint: if ScannerInformation.singlesHistogramLevel == SinglesHistogramLevelType.none, there should
121178# be no time blocks of this type in the stream.
122179# Otherwise, the union of SinglesHistogramTimeBlock.timeInterval should be at least as large as the union of all EventTimeBlocks.timeInterval
0 commit comments