8
8
#include < iostream>
9
9
#include < cmath>
10
10
#include < random>
11
+ #include < algorithm>
11
12
12
13
// (un)comment if you want HDF5 or binary output
13
14
#define USE_HDF5
@@ -20,12 +21,18 @@ using petsird::hdf5::PETSIRDWriter;
20
21
using petsird::binary::PETSIRDWriter;
21
22
#endif
22
23
24
+ #include " petsird_helpers.h"
25
+
23
26
// these are constants for now
24
27
constexpr uint32_t NUMBER_OF_ENERGY_BINS = 3 ;
25
28
constexpr uint32_t NUMBER_OF_TOF_BINS = 300 ;
26
29
constexpr float RADIUS = 400 .F;
27
- constexpr std::array<float , 3 > CRYSTAL_LENGTH{ 4 .F , 4 .F , 20 .F };
28
- constexpr std::array<float , 3 > NUM_CRYSTALS_PER_MODULE{ 5 , 6 , 2 };
30
+ constexpr std::array<float , 3 > CRYSTAL_LENGTH{ 20 .F , 4 .F , 4 .F };
31
+ constexpr std::array<float , 3 > NUM_CRYSTALS_PER_MODULE{ 2 , 4 , 5 };
32
+ constexpr uint32_t NUM_MODULES_ALONG_RING{ 20 };
33
+ constexpr uint32_t NUM_MODULES_ALONG_AXIS{ 2 };
34
+ constexpr float MODULE_AXIS_SPACING{ (NUM_CRYSTALS_PER_MODULE[2 ] + 4 ) * CRYSTAL_LENGTH[2 ] };
35
+
29
36
constexpr uint32_t NUMBER_OF_TIME_BLOCKS = 6 ;
30
37
constexpr float COUNT_RATE = 500 .F;
31
38
@@ -62,8 +69,8 @@ get_detector_module()
62
69
for (int rep2 = 0 ; rep2 < N2; ++rep2)
63
70
{
64
71
petsird::RigidTransformation transform{ { { 1.0 , 0.0 , 0.0 , RADIUS + rep0 * CRYSTAL_LENGTH[0 ] },
65
- { 0.0 , 1.0 , 0.0 , rep1 * CRYSTAL_LENGTH[1 ] },
66
- { 0.0 , 0.0 , 1.0 , rep2 * CRYSTAL_LENGTH[2 ] } } };
72
+ { 0.0 , 1.0 , 0.0 , ( rep1 - N1 / 2 ) * CRYSTAL_LENGTH[1 ] },
73
+ { 0.0 , 0.0 , 1.0 , ( rep2 - N2 / 2 ) * CRYSTAL_LENGTH[2 ] } } };
67
74
rep_volume.transforms .push_back (transform);
68
75
rep_volume.ids .push_back (rep0 + N0 * (rep1 + N1 * rep2));
69
76
}
@@ -85,25 +92,95 @@ get_scanner_geometry()
85
92
rep_module.object = get_detector_module ();
86
93
int module_id = 0 ;
87
94
std::vector<float > angles;
88
- for (int i = 0 ; i < 10 ; ++i)
95
+ for (unsigned int i = 0 ; i < NUM_MODULES_ALONG_RING ; ++i)
89
96
{
90
- angles.push_back (static_cast <float >(2 * M_PI * i / 10 ));
97
+ angles.push_back (static_cast <float >(( 2 * M_PI * i) / NUM_MODULES_ALONG_RING ));
91
98
}
92
99
for (auto angle : angles)
93
- {
94
- petsird::RigidTransformation transform{ { { std::cos (angle), std::sin (angle), 0 .F , 0 .F },
95
- { -std::sin (angle), std::cos (angle), 0 .F , 0 .F },
96
- { 0 .F , 0 .F , 1 .F , 0 .F } } };
97
- rep_module.ids .push_back (module_id++);
98
- rep_module.transforms .push_back (transform);
99
- }
100
+ for (unsigned ax_mod = 0 ; ax_mod < NUM_MODULES_ALONG_AXIS; ++ax_mod)
101
+ {
102
+ petsird::RigidTransformation transform{ { { std::cos (angle), std::sin (angle), 0 .F , 0 .F },
103
+ { -std::sin (angle), std::cos (angle), 0 .F , 0 .F },
104
+ { 0 .F , 0 .F , 1 .F , MODULE_AXIS_SPACING * ax_mod } } };
105
+ rep_module.ids .push_back (module_id++);
106
+ rep_module.transforms .push_back (transform);
107
+ }
100
108
}
101
109
petsird::ScannerGeometry scanner_geometry;
102
110
scanner_geometry.replicated_modules .push_back (rep_module);
103
111
scanner_geometry.ids .push_back (0 );
104
112
return scanner_geometry;
105
113
}
106
114
115
+ petsird::DetectionEfficiencies
116
+ get_detection_efficiencies (const petsird::ScannerInformation& scanner)
117
+ {
118
+ const auto num_det_els = petsird_helpers::get_num_det_els (scanner.scanner_geometry );
119
+ petsird::DetectionEfficiencies detection_efficiencies;
120
+
121
+ detection_efficiencies.det_el_efficiencies = xt::ones<float >({ num_det_els, scanner.NumberOfEnergyBins () });
122
+
123
+ // only 1 type of module in the current scanner
124
+ assert (scanner.scanner_geometry .replicated_modules .size () == 1 );
125
+ const auto & rep_module = scanner.scanner_geometry .replicated_modules [0 ];
126
+ const auto num_modules = rep_module.transforms .size ();
127
+
128
+ // We will only use rotational symmetries (no translation along the axis yet)
129
+ // We also assume all module-pairs are in coincidence, except those with the same angle.
130
+ // Writing a module number as (z-position, angle):
131
+ // eff((z1,a1), (z2, a2)) == eff((z1,0), (z2, abs(a2-a1)))
132
+ // or in linear indices
133
+ // eff(z1 + NZ * a1, z2 + NZ * a2) == eff(z1, z2 + NZ * abs(a2 - a1))
134
+ // (coincident) SGIDs need to start from 0, so ignoring self-coincident angles
135
+ constexpr auto num_SGIDs = NUM_MODULES_ALONG_AXIS * NUM_MODULES_ALONG_AXIS * (NUM_MODULES_ALONG_RING - 1 );
136
+ // SGID = z1 + NZ * (z2 + NZ * abs(a2 - a1) - 1)
137
+ constexpr auto NZ = NUM_MODULES_ALONG_AXIS;
138
+ detection_efficiencies.module_pair_sgidlut = yardl::NDArray<int , 2 >({ num_modules, num_modules });
139
+ auto & module_pair_SGID_LUT = *detection_efficiencies.module_pair_sgidlut ;
140
+ for (unsigned int mod1 = 0 ; mod1 < num_modules; ++mod1)
141
+ {
142
+ for (unsigned int mod2 = 0 ; mod2 < num_modules; ++mod2)
143
+ {
144
+ const auto z1 = mod1 % NZ;
145
+ const auto a1 = mod1 / NZ;
146
+ const auto z2 = mod2 % NZ;
147
+ const auto a2 = mod2 / NZ;
148
+ if (a1 == a2)
149
+ {
150
+ module_pair_SGID_LUT (mod1, mod2) = -1 ;
151
+ }
152
+ else
153
+ {
154
+ module_pair_SGID_LUT (mod1, mod2) = z1 + NZ * (z2 + NZ * (std::abs (int (a2) - int (a1)) - 1 ));
155
+ }
156
+ }
157
+ }
158
+ // assert(module_pair_SGID_LUT).max() == num_SGIDs - 1);
159
+
160
+ // assign an empty vector first, and reserve correct size
161
+ detection_efficiencies.module_pair_efficiencies_vector = petsird::ModulePairEfficienciesVector ();
162
+ detection_efficiencies.module_pair_efficiencies_vector ->reserve (num_SGIDs);
163
+
164
+ assert (rep_module.object .detecting_elements .size () == 1 );
165
+ const auto & detecting_elements = rep_module.object .detecting_elements [0 ];
166
+ const auto num_det_els_in_module = detecting_elements.transforms .size ();
167
+ for (unsigned int SGID = 0 ; SGID < num_SGIDs; ++SGID)
168
+ {
169
+ // extract first module_pair for this SGID. However, as this currently unused, it is commented out
170
+ // const auto& module_pair = *std::find(module_pair_SGID_LUT.begin(), module_pair_SGID_LUT.end(), SGID);
171
+ petsird::ModulePairEfficiencies module_pair_efficiencies;
172
+ module_pair_efficiencies.values = yardl::NDArray<float , 4 >(
173
+ { num_det_els_in_module, scanner.NumberOfEnergyBins (), num_det_els_in_module, scanner.NumberOfEnergyBins () });
174
+ // give some (non-physical) value
175
+ module_pair_efficiencies.values .fill (SGID);
176
+ module_pair_efficiencies.sgid = SGID;
177
+ detection_efficiencies.module_pair_efficiencies_vector ->emplace_back (module_pair_efficiencies);
178
+ assert (detection_efficiencies.module_pair_efficiencies_vector ->size () == unsigned (SGID + 1 ));
179
+ }
180
+
181
+ return detection_efficiencies;
182
+ }
183
+
107
184
petsird::ScannerInformation
108
185
get_scanner_info ()
109
186
{
@@ -133,6 +210,8 @@ get_scanner_info()
133
210
scanner_info.event_time_block_duration = 1 .F ; // ms
134
211
}
135
212
213
+ scanner_info.detection_efficiencies = get_detection_efficiencies (scanner_info);
214
+
136
215
return scanner_info;
137
216
}
138
217
@@ -154,12 +233,13 @@ get_header()
154
233
}
155
234
156
235
// return pair of integers between 0 and max
157
- std::pair< int , int >
236
+ std::array< unsigned , 2 >
158
237
get_random_pair (int max)
159
238
{
160
- int a = rand () % max;
161
- int b = rand () % max;
162
- return std::make_pair (a, b);
239
+ unsigned a = rand () % max;
240
+ unsigned b = rand () % max;
241
+ std::array<unsigned , 2 > p{ a, b };
242
+ return p;
163
243
}
164
244
165
245
uint32_t
@@ -175,16 +255,27 @@ get_random_tof_value()
175
255
}
176
256
177
257
std::vector<petsird::CoincidenceEvent>
178
- get_events (const petsird::Header&, std::size_t num_events)
258
+ get_events (const petsird::Header& header , std::size_t num_events)
179
259
{
180
260
std::vector<petsird::CoincidenceEvent> events;
181
261
events.reserve (num_events);
262
+ const auto num_det_els = petsird_helpers::get_num_det_els (header.scanner .scanner_geometry );
182
263
for (std::size_t i = 0 ; i < num_events; ++i)
183
264
{
184
- const auto detectors = get_random_pair (1 ); // TODO header.scanner.NumberOfDetectors());
185
265
petsird::CoincidenceEvent e;
186
- e.detector_ids [0 ] = detectors.first ;
187
- e.detector_ids [1 ] = detectors.second ;
266
+ // Generate random detector_ids, where the corresponding modules are distinct
267
+ while (true )
268
+ {
269
+ e.detector_ids = get_random_pair (num_det_els);
270
+ const auto mod_and_els = petsird_helpers::get_module_and_element (header.scanner .scanner_geometry , e.detector_ids );
271
+ if (!header.scanner .detection_efficiencies .module_pair_sgidlut /* there is no LUT */
272
+ || ((*header.scanner .detection_efficiencies .module_pair_sgidlut )(mod_and_els[0 ].module , mod_and_els[1 ].module )
273
+ >= 0 ))
274
+ {
275
+ // in coincidence, we can get out of the loop
276
+ break ;
277
+ }
278
+ }
188
279
e.energy_indices [0 ] = get_random_energy_value ();
189
280
e.energy_indices [1 ] = get_random_energy_value ();
190
281
e.tof_idx = get_random_tof_value ();
0 commit comments