CASToR  3.1
Tomographic Reconstruction (PET/SPECT/CT)
gDataConversionUtilities.cc
Go to the documentation of this file.
1 /*
2 This file is part of CASToR.
3 
4  CASToR is free software: you can redistribute it and/or modify it under the
5  terms of the GNU General Public License as published by the Free Software
6  Foundation, either version 3 of the License, or (at your option) any later
7  version.
8 
9  CASToR is distributed in the hope that it will be useful, but WITHOUT ANY
10  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12  details.
13 
14  You should have received a copy of the GNU General Public License along with
15  CASToR (in file GNU_GPL.TXT). If not, see <http://www.gnu.org/licenses/>.
16 
17 Copyright 2017-2020 all CASToR contributors listed below:
18 
19  --> Didier BENOIT, Claude COMTAT, Marina FILIPOVIC, Thibaut MERLIN, Mael MILLARDET, Simon STUTE, Valentin VIELZEUF, Zacharias CHALAMPALAKIS
20 
21 This is CASToR version 3.1.
22 */
23 
31 #include "gVariables.hh"
33 #include <iomanip>
34 
35 /*
36  Miscelleanous functionalities
37  */
38 
39 // =====================================================================
40 // ---------------------------------------------------------------------
41 // ---------------------------------------------------------------------
42 // =====================================================================
43 /*
44  \fn CheckGATECommand
45  \param a_key: string containing a GATE command to recover
46  \param a_line : string containing the line to check
47  \brief Check if the line contains the provided GATE command. In this case, \n
48  parse the line and returns the values in a vector of strings
49  \details Values are converted in mm if 'cm' is found in the line
50  \return the vector of strings containing the elements of the line.
51 */
52 vector<string> CheckGATECommand(const string& a_key, const string& a_line)
53 {
54  vector<string> values;
55 
56  // cut any part after comment symbol
57  string line = a_line;
58 
59  line = (line.find("#") != string::npos) ?
60  line.substr(0, line.find_first_of("#")) :
61  line;
62 
63  size_t foundAdress = line.find(a_key);
64 
65  if (foundAdress != string::npos)
66  {
67  values = Split(a_line);
68  values.erase (values.begin());
69  }
70 
71  ConvertValuesTomm(values);
72  return values;
73 }
74 
75 
76 
77 // =====================================================================
78 // ---------------------------------------------------------------------
79 // ---------------------------------------------------------------------
80 // =====================================================================
81 /*
82  \fn split
83  \param a_line : string to split
84  \brief Split the line provided in parameter into a vector of strings (separator is blankspace)
85  \return vector of string containing the splitted elements of the line
86 */
87 vector<string> Split(string a_line)
88 {
89  string buf;
90  stringstream ss(a_line);
91 
92  vector<string> tokens;
93 
94  while (ss >> buf)
95  tokens.push_back(buf);
96 
97  return tokens;
98 }
99 
100 
101 
102 // =====================================================================
103 // ---------------------------------------------------------------------
104 // ---------------------------------------------------------------------
105 // =====================================================================
106 /*
107  \fn ConvertValuesTomm
108  \param ap_v : vector of strings
109  \brief Check if the vector of strings passed in parameter contains the 'cm' unit \n
110  In this case, convert all its values in mm
111 */
112 void ConvertValuesTomm(vector<string>& ap_v)
113 {
114  // loop on values
115  for(uint16_t i=0; i < ap_v.size(); i++)
116  // check if values were provided in cm
117  if (ap_v[i] == "cm")
118  // Convert all values to mm (skip command key)
119  for (int j=0; j<i; j++)
120  {
121  float val;
122  ConvertFromString(ap_v[j], &val);
123  ap_v[j] = toString(10 * val);
124  }
125 }
126 
127 
128 // =====================================================================
129 // ---------------------------------------------------------------------
130 // ---------------------------------------------------------------------
131 // =====================================================================
132 /*
133  \fn WriteVector
134  \param file : the output file
135  \param a_key : key to write
136  \param a_vals : vector containing the key values
137  \brief Write the key and its values in the file provided in parameter
138  \return 0 if success, positive value otherwise
139 */
140 template <class T>
141 int WriteVector(ofstream& file, const string& a_key, vector <T> a_vals)
142 {
143  int n = a_vals.size();
144 
145  if (n > 0)
146  {
147  file << a_key ;
148  for (int i=0; i < n; i++)
149  {
150  stringstream ss;
151  ss << a_vals[i];
152  if (i == n-1)
153  file << ss.str() << endl;
154  else
155  file << ss.str() << ",";
156  }
157  }
158  else
159  file << a_key << "0" <<endl;
160 
161  return 0;
162 }
163 
164 template int WriteVector(ofstream& file, const string& a_key, vector <double> a_vals);
165 
166 
167 
168 // =====================================================================
169 // ---------------------------------------------------------------------
170 // ---------------------------------------------------------------------
171 // =====================================================================
172 /*
173  \fn WriteVector
174  \param file : the output file
175  \param a_key : key to write
176  \param a_vals : vector containing the key values
177  \brief Write the key and its values in the file provided in parameter
178  \return 0 if success, positive value otherwise
179 */
180 int WriteVector(ofstream& file, const string& a_key, vector <string> a_vals)
181 {
182  int n = a_vals.size();
183 
184  if (n > 0)
185  {
186  file << a_key ;
187  for (int i=0; i < n; i++)
188  {
189  if (i == n-1)
190  file << a_vals[i] << endl;
191  else
192  file << a_vals[i] << ",";
193  }
194  }
195  else
196  file << a_key << "0" <<endl;
197 
198  return 0;
199 }
200 
201 
202 
203 // =====================================================================
204 // ---------------------------------------------------------------------
205 // ---------------------------------------------------------------------
206 // =====================================================================
207 /*
208  \fn WriteVector
209  \param file : the output file
210  \param a_key : key to write
211  \param a_vals : vector containing the key values
212  \brief Write the key and its values contained in a 2 level vector of strings in the file provided in parameter
213  \return 0 if success, positive value otherwise
214 */
215 int WriteVector(ofstream& file, const string& a_key, vector <vector<string> > a_vals)
216 {
217  int n = a_vals.size();
218  file << a_key;
219  for (int i=0; i < n; i++)
220  for (int j=0; j < 3; j++)
221  if (i == n-1 && j == 2)
222  file << a_vals[i][j] << endl;
223  else
224  file << a_vals[i][j] << ",";
225 
226  return 0;
227 }
228 
229 
230 
231 
232 // =====================================================================
233 // ---------------------------------------------------------------------
234 // ---------------------------------------------------------------------
235 // =====================================================================
236 /*
237  \fn GetGATEMacFiles(const string& a_pathMac, vector<string> ap_pathToMacFiles)
238  \param a_pathMac : path to a GATE main macro file
239  \param ap_pathToMacFiles : array containing the paths of macro files
240  \brief Extract the paths to each macro file contained in the main macro file.
241  \return 0 if success, positive value otherwise
242 */
243 int GetGATEMacFiles(const string& a_pathMac, vector<string> &ap_pathToMacFiles)
244 {
245  ifstream mac_file(a_pathMac, ios::in);
246 
247  if(mac_file)
248  {
249  string quickLine;
250 
251  while(getline(mac_file, quickLine))
252  {
253  vector <string> values;
254 
255  values = CheckGATECommand("/control/execute", quickLine);
256 
257  if (values.size()>0)
258  {
259  // Check if a full path has been provided (start with '/' )
260  // Give just values in this case
261  char first_char = values[0].at(0);
262 
263  if(first_char == '/')
264  ap_pathToMacFiles.push_back(values[0]);
265  // Just concatenate path otherwise
266  else
267  ap_pathToMacFiles.push_back(GetPathOfFile(a_pathMac)+values[0]);
268 
269  }
270  }
271  }
272  else
273  {
274  Cerr("***** GetGATEMacFiles() -> Couldn't open mac file "<< a_pathMac << " !" << endl);
275  return -1;
276  }
277 
278  mac_file.close();
279  return 0;
280 }
281 
282 
283 
284 
285 // =====================================================================
286 // ---------------------------------------------------------------------
287 // ---------------------------------------------------------------------
288 // =====================================================================
289 /*
290  \fn GetGATESystemType
291  \param a_pathMac : path to a GATE macro file
292  \brief Read a GATE macro file and identify the system type from the 'gate/systems/' command lines
293  \return system type, as described by the macro GATE_SYS_TYPE, -1 if error
294 */
295 int GetGATESystemType(const string& a_pathMac)
296 {
297  int system_type = GATE_SYS_UNKNOWN;
298 
299  // Recover path to all macro files from the main mac file
300  vector<string> path_mac_files;
301  path_mac_files.push_back(a_pathMac);
302 
303  if(GetGATEMacFiles(a_pathMac , path_mac_files))
304  {
305  Cerr("***** GetGATESystemType() ->Error occurred when trying to recover paths to GATE macro files !" << endl);
306  return -1;
307  }
308 
309  for(uint16_t f=0 ; f<path_mac_files.size() ; f++)
310  cout << f << " : " << path_mac_files[f] << endl;
311 
312  // Loop on all macro files
313  for(uint16_t f=0 ; f<path_mac_files.size() ; f++)
314  {
315  ifstream mac_file(path_mac_files[f], ios::in);
316 
317  if(mac_file)
318  {
319  string line;
320  while(getline(mac_file, line))
321  {
322  // Check a 'gate/systems' command line
323  if(line.find("/gate/systems/") != string::npos )
324  {
325  if(line.find("/gate/systems/ecat") != string::npos )
326  system_type = GATE_SYS_ECAT;
327 
328  if(line.find("/gate/systems/cylindricalPET") != string::npos )
329  system_type = GATE_SYS_CYLINDRICAL;
330 
331  if(line.find("/gate/systems/SPECThead") != string::npos )
332  system_type = GATE_SYS_SPECT;
333 
334  if(line.find("/gate/systems/OPET") != string::npos ||
335  line.find("/gate/systems/CTSCANNER") != string::npos ||
336  line.find("/gate/systems/CPET") != string::npos ||
337  line.find("/gate/systems/ecatAccel") != string::npos ||
338  line.find("/gate/systems/OpticalSystem") != string::npos )
339  {
340  Cerr("unsupported system detected (line = " << line <<") ! "<< endl);
341  Cerr("supported systems for this script are cylindricalPET, SPECThead, and ecat" << endl);
342  }
343  }
344  }
345  }
346  else
347  {
348  Cerr("***** GetGATESystemType() -> Error : Couldn't open mac file "<< path_mac_files[f] << " !" << endl);
349  return -1;
350  }
351 
352  mac_file.close();
353  }
354 
355  return system_type;
356 }
357 
358 
359 
360 
361 // =====================================================================
362 // ---------------------------------------------------------------------
363 // ---------------------------------------------------------------------
364 // =====================================================================
365 /*
366  \fn GetGATEAliasesCylindrical(vector<string> path_mac_files
367  string& rsector_name,
368  string& module_name,
369  string& submodule_name,
370  string& crystal_name,
371  vector<string>& layers_name,
372  int vb )
373  \param path_mac_files
374  \param rsector_name
375  \param module_name
376  \param submodule_name
377  \param crystal_name
378  \param layers_name
379  \param vb
380  \brief Loop over a list of path to GATE macro files passed in parameter
381  to recover aliases of the different parts of a cylindricalPET
382  \return 0 if success, positive value otherwise
383 */
384 int GetGATEAliasesCylindrical(vector<string> path_mac_files,
385  string& rsector_name,
386  string& module_name,
387  string& submodule_name,
388  string& crystal_name,
389  vector<string>& layers_name,
390  int vb )
391 {
392  int depth = 0;
393 
394  for(uint16_t f=0 ; f<path_mac_files.size() ; f++)
395  {
396  ifstream mac_file(path_mac_files[f].c_str(), ios::in);
397 
398  if(mac_file)
399  {
400  string quickLine;
401  while(getline(mac_file, quickLine))
402  {
403  vector <string> values;
404 
405  values = CheckGATECommand("/gate/systems/cylindricalPET/rsector/attach", quickLine);
406 
407  if (values.size()>0)
408  {
409  rsector_name = values[0];
410  depth++;
411  }
412 
413  values = CheckGATECommand("/gate/systems/cylindricalPET/module/attach", quickLine);
414  if (values.size()>0)
415  {
416  module_name = values[0];
417  depth++;
418  }
419 
420  values = CheckGATECommand("/gate/systems/cylindricalPET/submodule/attach", quickLine);
421  if (values.size()>0)
422  {
423  submodule_name = values[0];
424  depth++;
425  }
426 
427  values = CheckGATECommand("/gate/systems/cylindricalPET/crystal/attach", quickLine);
428  if (values.size()>0)
429  {
430  crystal_name = values[0];
431  depth++;
432  }
433 
434  // layers
435  for(int l=0 ; l<GATE_NB_MAX_LAYERS ; l++)
436  {
437  stringstream ss;
438  ss << l;
439  values = CheckGATECommand("/gate/systems/cylindricalPET/layer"+ss.str()+"/attach", quickLine);
440  if (values.size()>0)
441  {
442  layers_name.push_back(values[0]);
443  depth++;
444  }
445  }
446 
447  }
448  }
449  else
450  {
451  Cerr("***** GetGATEAliasesCylindrical()->Couldn't open mac file "<< path_mac_files[f] << " !" << endl);
452  return 1;
453  }
454 
455  mac_file.close();
456  }
457 
458 
459  // Check we have the required number of elements for the system
460  if (depth < 2)
461  {
462  Cout("***** GetGATEAliasesCylindrical() :: Error : Missing elements in the system architecture" << endl <<
463  " At least two of the following lines are required :" << endl <<
464  " - /gate/systems/cylindricalPET/rsector/attach" << endl <<
465  " - /gate/systems/cylindricalPET/module/attach" << endl <<
466  " - /gate/systems/cylindricalPET/submodule/attach" << endl <<
467  " - /gate/systems/cylindricalPET/crystal/attach" << endl <<
468  " - /gate/systems/cylindricalPET/layeri[i=0..3]/attach" << endl);
469  return 1;
470  }
471  else
472  {
473  // Interpret the first element as the rsector
474  if(rsector_name.empty())
475  {
476  if(module_name.empty())
477  {
478  //submodule as rsector
479  rsector_name = submodule_name;
480  submodule_name = "";
481  }
482  else
483  {
484  //module as rsector
485  rsector_name = module_name;
486  module_name = "";
487  }
488  }
489 
490  if(vb >= 2)
491  {
492  if(!rsector_name.empty()) Cout("Detected rsector container's name : " << rsector_name << endl);
493  if(!module_name.empty()) Cout("Detected module container's name : " << module_name << endl);
494  if(!submodule_name.empty()) Cout("Detected submodule container's name : " << submodule_name << endl);
495  if(!crystal_name.empty()) Cout("Detected crystal container's name : " << crystal_name << endl);
496  for(size_t l=0 ; l<layers_name.size() ; l++)
497  if(!layers_name[l].empty()) Cout("Detected layer #"<< l << " container's name : " << layers_name[l] << endl);
498  }
499  }
500 
501  return 0;
502 }
503 
504 
505 
506 
507 // =====================================================================
508 // ---------------------------------------------------------------------
509 // ---------------------------------------------------------------------
510 // =====================================================================
511 /*
512  \fn GetGATEAliasesEcat(vector<string> path_mac_files,
513  string& block_name,
514  string& crystal_name,
515  int vb )
516  \param path_mac_files
517  \param block_name
518  \param crystal_name
519  \param vb
520  \brief Loop over a list of path to GATE macro files passed in parameter
521  to recover aliases of the different parts of an ecat system
522  \return 0 if success, positive value otherwise
523 */
524 int GetGATEAliasesEcat(vector<string> path_mac_files,
525  string& block_name,
526  string& crystal_name,
527  int vb )
528 {
529  int depth = 0;
530 
531  for(uint16_t f=0 ; f<path_mac_files.size() ; f++)
532  {
533  ifstream mac_file(path_mac_files[f].c_str(), ios::in);
534 
535  if(mac_file)
536  {
537  // Reading the .mac file line by line and finding the names of the different parts of the architecture
538  string quickLine;
539  while(getline(mac_file, quickLine))
540  {
541  vector <string> values;
542 
543  values = CheckGATECommand("/gate/systems/ecat/block/attach", quickLine);
544  if (values.size()>0)
545  {
546  block_name = values[0];
547  depth++;
548  }
549 
550  values = CheckGATECommand("/gate/systems/ecat/crystal/attach", quickLine);
551  if (values.size()>0)
552  {
553  crystal_name = values[0];
554  depth++;
555  }
556  }
557  }
558  else
559  {
560  Cerr("***** GetGATEAliasesEcat()-> Couldn't open mac file "<< path_mac_files[f].c_str()<< " !" << endl);
561  return 1;
562  }
563  }
564 
565 
566  // Check we have the required number of elements for the system
567  if (depth < 2)
568  {
569  Cerr("***** GetGATEAliasesEcat() :: Error : Missing elements in the system architecture" << endl
570  << " The following lines are required :" << endl
571  << " - /gate/systems/ecat/block/attach" << endl
572  << " - /gate/systems/ecat/crystal/attach" << endl);
573  return 1;
574  }
575  else
576  {
577  if(vb >= 2)
578  Cout("First container's name (usually block) is : " << block_name << endl
579  << "Second container's name (usually crystal) is : " << crystal_name << endl);
580  }
581 
582  return 0;
583 }
584 
585 
586 
587 
588 
589 // =====================================================================
590 // ---------------------------------------------------------------------
591 // ---------------------------------------------------------------------
592 // =====================================================================
593 /*
594  \fn GetGATEAliasesSPECT(vector<string> path_mac_files,
595  string& base_name,
596  string& crystal_name,
597  string& pixel_name,
598  int vb )
599  \param path_mac_files
600  \param base_name
601  \param crystal_name
602  \param pixel_name
603  \param vb
604  \brief Loop over a list of path to GATE macro files passed in parameter
605  to recover aliases of the different parts of an ecat system
606  \return 0 if success, positive value otherwise
607 */
608 int GetGATEAliasesSPECT(vector<string> path_mac_files,
609  string& base_name,
610  string& crystal_name,
611  string& pixel_name,
612  int vb )
613 {
614  int depth = 0;
615 
616  for(uint16_t f=0 ; f<path_mac_files.size() ; f++)
617  {
618  ifstream mac_file(path_mac_files[f].c_str(), ios::in);
619 
620  if(mac_file)
621  {
622  // Reading the .mac file line by line and finding the names of the different parts of the architecture
623  string quickLine;
624 
625  while(getline(mac_file, quickLine))
626  {
627  vector <string> values;
628 
629  values = CheckGATECommand("/gate/systems/SPECThead/base/attach", quickLine);
630  if (values.size()>0)
631  {
632  base_name = values[0];
633  depth++;
634  }
635 
636  values = CheckGATECommand("/gate/systems/SPECThead/crystal/attach", quickLine);
637  if (values.size()>0)
638  {
639  crystal_name = values[0];
640  depth++;
641  }
642 
643  values = CheckGATECommand("/gate/systems/SPECThead/pixel/attach", quickLine);
644  if (values.size()>0)
645  {
646  pixel_name = values[0];
647  depth++;
648  }
649  }
650 
651  }
652  else
653  {
654  Cerr("***** GetGATEAliasesSPECT()-> Couldn't open mac file "<< path_mac_files[f].c_str()<< " !" << endl);
655  return 1;
656  }
657  }
658 
659  // Check we have the required number of elements for the system
660  if (depth < 1)
661  {
662  Cerr("***** GetGATEAliasesSPECT() :: Error : Missing elements in the system architecture" << endl
663  << " The following line is required :" << endl
664  << " - /gate/systems/SPECThead/crystal/attach" << endl);
665  return 1;
666  }
667  else
668  {
669  if(vb >= 2)
670  Cout("Crystal container's name is : " << crystal_name << endl);
671  }
672 
673  return 0;
674 }
675 
676 
677 
678 
679 // =====================================================================
680 // ---------------------------------------------------------------------
681 // ---------------------------------------------------------------------
682 // =====================================================================
683 /*
684  \fn ConvertIDecat
685  \param nBlocksPerRing
686  \param nBlocksLine
687  \param nCrystalsTransaxial
688  \param nCrystalsAxial
689  \param crystalID
690  \param blockID
691  \brief Compute a CASToR crystal index of a GATE ecat system from its indexes (block/crystal) and the system layout
692  \return CASToR crystal index
693 */
694 uint32_t ConvertIDecat( int32_t nBlocksPerRing,
695  int32_t nBlocksLine,
696  int32_t nCrystalsTransaxial,
697  int32_t nCrystalsAxial,
698  int32_t crystalID,
699  int32_t blockID)
700 {
701  int32_t nCrystalsPerRing = nBlocksPerRing * nCrystalsTransaxial;
702 
703  int32_t ringID = (int32_t)( blockID/nBlocksPerRing ) * nCrystalsAxial
704  + (int32_t)( crystalID/nCrystalsTransaxial );
705 
706  int32_t castorID = nCrystalsPerRing * ringID
707  + nCrystalsTransaxial*( blockID % nBlocksPerRing )
708  + crystalID % nCrystalsTransaxial;
709 
710  return castorID;
711 }
712 
713 
714 
715 // =====================================================================
716 // ---------------------------------------------------------------------
717 // ---------------------------------------------------------------------
718 // =====================================================================
719 /*
720  \fn ConvertIDSPECTRoot1
721  \param a_headID : head index as provided in the root file
722  \param a_rotAngle : rotation angle (deg) as provided in the root file
723  \param a_angStep : angular step (deg) computed from the macro file
724  \param a_nProjectionsByHead : total number of projections for each head
725  \brief Compute a CASToR projection index of a GATE SPECThead system
726  \return CASToR crystal index
727 */
728 uint32_t ConvertIDSPECTRoot1( int32_t a_headID,
729  float_t a_rotAngle,
730  float_t a_angStep,
731  uint32_t a_nProjectionsByHead)
732 {
733  // Compute angular index from the angle position of the head and the angular step
734  int32_t angID = round(a_rotAngle/a_angStep);
735 
736  // Get final index for this head
737  int32_t castorID = a_headID*a_nProjectionsByHead + angID;
738 
739  return castorID;
740 }
741 
742 
743 
744 
745 // =====================================================================
746 // ---------------------------------------------------------------------
747 // ---------------------------------------------------------------------
748 // =====================================================================
749 /*
750  \fn ConvertIDSPECTRoot2
751  \param a_nbSimulatedPixels
752  \param a_nPixTrs
753  \param a_nPixAxl
754  \param a_headID
755  \param a_crystalID
756  \param a_pixelID
757  \param a_rotAngle
758  \param a_headAngPitch
759  \param a_crystalSizeAxl
760  \param a_crystalSizeTrs
761  \param a_gPosX
762  \param a_gPosY
763  \param a_gPosZ
764  \brief Compute a CASToR crystal index of a GATE SPECThead system
765  \return CASToR crystal index. Return a higher number than the max index if error
766 */
767 uint32_t ConvertIDSPECTRoot2( uint32_t a_nbSimulatedPixels,
768  uint32_t a_nPixTrs,
769  uint32_t a_nPixAxl,
770  int32_t a_headID,
771  int32_t a_crystalID,
772  int32_t a_pixelID,
773  float_t a_rotAngle,
774  float_t a_headAngPitch,
775  float_t a_crystalSizeAxl,
776  float_t a_crystalSizeTrs,
777  float_t a_gPosX,
778  float_t a_gPosY,
779  float_t a_gPosZ)
780 {
781  int32_t castorID = 0;
782 
783  // we have a pixel matrix, just return the pixelID
784  if (a_nbSimulatedPixels > 1)
785  {
786  castorID = a_pixelID;
787  }
788  // Compute the pixelID according to the global XYZ positions in the crystal
789  // Cheap implementation. Does not take the DOI into account (would depend on collimator)
790  else
791  {
792  // Compute pixel sizes
793  FLTNB sizePixTrs = a_crystalSizeTrs/a_nPixTrs;
794  FLTNB sizePixAxl = a_crystalSizeAxl/a_nPixAxl;
795 
796  // Compute axial ID
797  uint32_t axialID = (uint32_t)(( a_gPosZ + a_nPixAxl/2*sizePixAxl) / sizePixAxl);
798 
799  // Compute transaxial ID
800  // Compute head position angle ( in GATE referential)
801  float_t ang = a_headID*a_headAngPitch + a_rotAngle;
802 
803  // Get transaxial position
804  float_t sin_a = sin(-ang*M_PI/180);
805  float_t cos_a = cos(-ang*M_PI/180);
806  float_t trs_pos = a_gPosX*sin_a + a_gPosY*cos_a ;
807 
808  // Compute transaxial ID
809  uint32_t transID = (uint32_t)(( trs_pos + a_nPixTrs/2*sizePixTrs) / sizePixTrs);
810 
811  if ( axialID < a_nPixAxl && transID < a_nPixTrs )
812  {
813  castorID = axialID*a_nPixTrs + transID;
814  }
815  else // return more than the max crystal ID to check for errors
816  {
817  castorID = a_nPixAxl*a_nPixTrs;
818  }
819 
820  }
821 
822  return castorID;
823 }
824 
825 
826 
827 
828 uint32_t ConvertIDcylindrical(uint32_t nRsectorsAngPos,
829  uint32_t nRsectorsAxial,
830  bool a_invertDetOrder,
831  int a_rsectorIdOrder,
832  uint32_t nModulesTransaxial,
833  uint32_t nModulesAxial,
834  uint32_t nSubmodulesTransaxial,
835  uint32_t nSubmodulesAxial,
836  uint32_t nCrystalsTransaxial,
837  uint32_t nCrystalsAxial,
838  uint8_t nLayers,
839  uint32_t* nCrystalPerLayer,
840  vector<uint32_t> nLayersRptTransaxial,
841  vector<uint32_t> nLayersRptAxial,
842  int32_t layerID,
843  int32_t crystalID,
844  int32_t submoduleID,
845  int32_t moduleID,
846  int32_t rsectorID)
847 {
848  // Castor ID definition
849  uint32_t castorID = 0;
850  uint8_t layer = 0;
851 
852 
853  if(nLayersRptTransaxial.size()==0 && nLayersRptAxial.size()==0)
854  {
855  // layerID represents the actual layer level
856 
857  // add the number of crystals contained in previous layers as
858  // CASToR indexes all crystals of a layer ring before the next layer
859  layer = layerID;
860 
861  for(int l=0 ; l<layer; l++)
862  castorID += nCrystalPerLayer[l];
863 
864  int32_t nTrsCrystalsPerSubmodule = nCrystalsTransaxial;
865  int32_t nTrsCrystalsPerModule = nTrsCrystalsPerSubmodule * nSubmodulesTransaxial;
866  int32_t nTrsCrystalsPerRsector = nTrsCrystalsPerModule * nModulesTransaxial;
867  int32_t nCrystalsPerRing = nTrsCrystalsPerRsector * nRsectorsAngPos;
868 
869  // Rsector axial(=ring) and transaxial(=angular) ID
870  // Fastest ordering orientation (axial or transaxial) depends on the repeaters
871  int32_t rsectorAxlID = 0 ;
872  int32_t rsectorTrsID = 0 ;
873 
874  // standard, transaxial first
875  if(a_rsectorIdOrder == 0)
876  {
877  rsectorAxlID = rsectorID/nRsectorsAngPos ;
878  rsectorTrsID = (int32_t)(rsectorID%nRsectorsAngPos) ;
879  }
880  else // using cubic array, axial first
881  {
882  rsectorAxlID = rsectorID%nRsectorsAxial ;
883  rsectorTrsID = (int32_t)(rsectorID/nRsectorsAxial) ;
884  }
885 
886 
887  // Compute axial ID
888  int32_t ringID = rsectorAxlID * nModulesAxial * nSubmodulesAxial * nCrystalsAxial
889  + (int32_t)(moduleID/nModulesTransaxial) * nSubmodulesAxial * nCrystalsAxial
890  + (int32_t)(submoduleID/nSubmodulesTransaxial) * nCrystalsAxial
891  + (int32_t)(crystalID/nCrystalsTransaxial);
892 
893  // Recover transaxial ID for each element
894  moduleID = moduleID % nModulesTransaxial;
895  submoduleID = submoduleID % nSubmodulesTransaxial;
896  crystalID = crystalID % nCrystalsTransaxial;
897 
898  // Reverse transaxial ordering
899  if( a_invertDetOrder )
900  {
901  moduleID = nModulesTransaxial-1 - moduleID;
902  submoduleID = nSubmodulesTransaxial-1 - submoduleID;
903  crystalID = nCrystalsTransaxial-1 - crystalID;
904  }
905 
906  // Compute final ID
907  castorID += nCrystalsPerRing * ringID
908  + nTrsCrystalsPerRsector * rsectorTrsID
909  + nTrsCrystalsPerModule * moduleID
910  + nTrsCrystalsPerSubmodule * submoduleID
911  + crystalID;
912  }
913 
914  else
915  {
916  // layerID represents a crystal layer element
917 
918  // Get the total number of crystals in the first layer
919  uint32_t sum_detectors_prev_layers = 0;
920 
921  // Get the layer which the crystal belongs to
922  // (Compare layer ID to sum of nb detectors in previous layors +
923  // detectors in actual layers)
924  while ( layerID >= (int32_t)( sum_detectors_prev_layers
925  + ( nLayersRptTransaxial[layer]
926  * nLayersRptAxial[layer]) ) )
927  {
928  // recover nb detectors in previous layers
929  sum_detectors_prev_layers += nLayersRptTransaxial[layer]
930  * nLayersRptAxial[layer];
931 
932  layer++; // increment layer
933  }
934 
935  // layerID contain index of all the detectors in all layer levels
936  // if not in the 1st layer, substract the IDs of the previous layers
937  // so that layerID variable indexes detectors only in the actual layer
938  if (layer>0)layerID -= sum_detectors_prev_layers;
939 
940  // add the number of crystals contained in previous layers as
941  // CASToR indexes all crystals of a layer ring before the next layer
942  for(int l=0 ; l<layer ; l++)
943  castorID += nCrystalPerLayer[l];
944 
945  int32_t nTrsCrystalsPerSubmodule = nCrystalsTransaxial * nLayersRptTransaxial[layer];
946  int32_t nTrsCrystalsPerModule = nTrsCrystalsPerSubmodule * nSubmodulesTransaxial;
947  int32_t nTrsCrystalsPerRsector = nTrsCrystalsPerModule * nModulesTransaxial;
948  int32_t nCrystalsPerRing = nTrsCrystalsPerRsector * nRsectorsAngPos;
949 
950  // Rsector axial(=ring) and transaxial(=angular) ID
951  // Fastest ordering orientation (axial or transaxial) depends on the repeaters
952  int32_t rsectorAxlID = 0 ;
953  int32_t rsectorTrsID = 0 ;
954 
955  // standard, transaxial first
956  if(a_rsectorIdOrder == 0)
957  {
958  rsectorAxlID = rsectorID/nRsectorsAngPos ;
959  rsectorTrsID = (int32_t)(rsectorID%nRsectorsAngPos) ;
960  }
961  else // using cubic array, axial first
962  {
963  rsectorAxlID = rsectorID%nRsectorsAxial ;
964  rsectorTrsID = (int32_t)(rsectorID/nRsectorsAxial) ;
965  }
966 
967 
968  // Compute axial ID
969  int32_t ringID = rsectorAxlID * nModulesAxial * nSubmodulesAxial * nCrystalsAxial * nLayersRptAxial[layer]
970  + (int32_t)(moduleID/nModulesTransaxial) * nSubmodulesAxial * nCrystalsAxial * nLayersRptAxial[layer]
971  + (int32_t)(submoduleID/nSubmodulesTransaxial) * nCrystalsAxial * nLayersRptAxial[layer]
972  + (int32_t)(crystalID/nCrystalsTransaxial) * nLayersRptAxial[layer];
973 
974  // Add layer contribution to axial ID
975  if(!nLayersRptTransaxial.empty() )
976  ringID += (int32_t)(layerID/nLayersRptTransaxial[layer]);
977 
978 
979  // Recover transaxial ID for each element
980  moduleID = moduleID % nModulesTransaxial;
981  submoduleID = submoduleID % nSubmodulesTransaxial;
982  crystalID = crystalID % nCrystalsTransaxial;
983  layerID = layerID % nLayersRptTransaxial[layer];
984 
985  // Reverse transaxial ordering
986  if( a_invertDetOrder )
987  {
988  moduleID = nModulesTransaxial-1 - moduleID;
989  submoduleID = nSubmodulesTransaxial-1 - submoduleID;
990  crystalID = nCrystalsTransaxial-1 - crystalID;
991  layerID = nLayersRptTransaxial[layer]-1 - layerID;
992  }
993 
994  // Compute final ID
995  castorID += nCrystalsPerRing * ringID
996  + nTrsCrystalsPerRsector * rsectorTrsID
997  + nTrsCrystalsPerModule * moduleID
998  + nTrsCrystalsPerSubmodule * submoduleID
999  + crystalID
1000  + layerID;
1001  }
1002 
1003  return castorID;
1004 }
1005 
1006 
1007 
1008 // =====================================================================
1009 // ---------------------------------------------------------------------
1010 // ---------------------------------------------------------------------
1011 // =====================================================================
1012 /*
1013  \fn ComputeKindGATEEvent
1014  \param eventID1
1015  \param eventID2
1016  \param comptonPhantom1
1017  \param comptonPhantom2
1018  \param rayleighPhantom1
1019  \param rayleighPhantom2
1020  \brief Determine kind of a given coincidence event, from its attributes
1021  \return kind of the coincidence : unknown (=0, default), true(=1), single scat(=2), multiple scat(=3), random(=4)) (default value =0)
1022 */
1023 int ComputeKindGATEEvent(uint32_t eventID1, uint32_t eventID2,
1024  int comptonPhantom1, int comptonPhantom2,
1025  int rayleighPhantom1, int rayleighPhantom2)
1026 {
1027  if (eventID1 != eventID2)
1028  //random
1029  return KIND_RDM;
1030  else
1031  {
1032  if (comptonPhantom1 == 0 && comptonPhantom2 == 0 &&
1033  rayleighPhantom1 == 0 && rayleighPhantom2 == 0)
1034  //true
1035  return KIND_TRUE;
1036  else
1037  {
1038  if (comptonPhantom1 == 1 || comptonPhantom2 == 1 ||
1039  rayleighPhantom1 == 1 || rayleighPhantom2 == 1)
1040  //single scat
1041  return KIND_SCAT;
1042  if (comptonPhantom1 > 1 || comptonPhantom2 > 1 ||
1043  rayleighPhantom1 > 1 || rayleighPhantom2 > 1)
1044  //multiple scat
1045  return KIND_MSCAT;
1046  }
1047  }
1048  // unknown
1049  return KIND_UNKNOWN;
1050 }
1051 
1052 
1053 
1054 // =====================================================================
1055 // ---------------------------------------------------------------------
1056 // ---------------------------------------------------------------------
1057 // =====================================================================
1058 /*
1059  \fn ReadMacCylindrical
1060  \param a_pathMac : path to a macro file
1061  \param nLayers : nb of crystal layers
1062  \param nCrystalsAxial : nb of axial crystals (in a submodule)
1063  \param nCrystalsTransaxial : nb of transaxial crystals (in a submodule)
1064  \param nLayersRptAxial : Array containing the number of axial crystals in each layer
1065  \param nLayersRptTransaxial : Array containing the number of transaxial crystals in each layer
1066  \param nSubmodulesAxial : nb of axial submodules (in a module)
1067  \param nSubmodulesTransaxial : nb of transaxial submodules (in a module)
1068  \param nModulesAxial : nb of axial modules (in a rsector)
1069  \param nModulesTransaxial : nb of transaxial modules (in a rsector)
1070  \param nRsectorsAxial : nb of axial rsectors
1071  \param nRsectorsAngPos : nb of rsectors per ring
1072  \param inverted_det_order : reverse ordering orientation of detectors depending on first rsector location
1073  \param rsector_id_order : ordering of rsector id
1074  \param start_time_ms : acquisition start time converted in ms
1075  \param duration_ms : acquisition duration converted in ms
1076  \param pet_coinc_window : coincidence window (required for TOF parameters)
1077  \param vb : verbosity
1078  \brief Recover informations about the scanner element of a cylindricalPET system and acquisition duration, from a GATE macro file
1079  \return 0 if success, positive value otherwise
1080 */
1081 int ReadMacCylindrical(string a_pathMac,
1082  uint8_t &nLayers,
1083  uint32_t *nb_crystal_per_layer,
1084  uint32_t &nCrystalsTot,
1085  uint32_t &nCrystalsAxial,
1086  uint32_t &nCrystalsTransaxial,
1087  vector<uint32_t> &nLayersRptAxial,
1088  vector<uint32_t> &nLayersRptTransaxial,
1089  uint32_t &nSubmodulesAxial,
1090  uint32_t &nSubmodulesTransaxial,
1091  uint32_t &nModulesAxial,
1092  uint32_t &nModulesTransaxial,
1093  uint32_t &nRsectorsAxial,
1094  uint32_t &nRsectorsAngPos,
1095  bool &invert_det_order,
1096  int &rsector_id_order,
1097  uint32_t &start_time_ms,
1098  uint32_t &duration_ms,
1099  FLTNB &pet_coinc_window,
1100  int vb)
1101 {
1102  vector<string> path_mac_files;
1103  path_mac_files.push_back(a_pathMac);
1104 
1105  // Recover path to all macro files from the main mac file
1106  if(GetGATEMacFiles(a_pathMac , path_mac_files))
1107  {
1108  Cerr("***** GetGATESystemType() ->Error occurred when trying to recover paths to GATE macro files !" << endl);
1109  return 1;
1110  }
1111 
1112  string rsector_name = "";
1113  string module_name = "";
1114  string submodule_name = "";
1115  string crystal_name = "";
1116  string mod_rptr_type = "cubicArray";
1117  string smod_rptr_type = "cubicArray";
1118  string cry_rptr_type = "cubicArray";
1119  string lay_rptr_type = "cubicArray";
1120 
1121  vector <string> layers_name;
1122  bool is_rsector_Y_axis = false;
1123 
1124  // Recover aliases of the different parts of the architecture
1125  if(GetGATEAliasesCylindrical(path_mac_files, rsector_name, module_name, submodule_name, crystal_name, layers_name, vb) )
1126  {
1127  Cerr("***** GetGATESystemType() ->Error occurred when trying to recover aliases for the elements of the cylindricalPET !" << endl);
1128  return 1;
1129  }
1130 
1131  // Recover nb of detected layers
1132  nLayers = layers_name.size();
1133 
1134  // Loop to recover all other informations
1135  for(uint16_t f=0 ; f<path_mac_files.size() ; f++)
1136  {
1137  ifstream mac_file(path_mac_files[f].c_str(), ios::in);
1138 
1139  string line;
1140  double time_start =-1.,
1141  time_stop =-1.,
1142  time_slices =-1.;
1143 
1144  while(getline(mac_file, line))
1145  {
1146  vector <string> values;
1147  string kword ="";
1148 
1149 
1150  // RSECTORS
1151 
1152  kword = "/gate/"+rsector_name+"/placement/setTranslation";
1153  values = CheckGATECommand(kword, line);
1154 
1155  // Check where the first rsector has been created
1156  if (values.size()>0)
1157  {
1158  FLTNB rsector_pos_X =0.,
1159  rsector_pos_Y =0.;
1160 
1161  if(ConvertFromString(values[0], &rsector_pos_X) ||
1162  ConvertFromString(values[1], &rsector_pos_Y) )
1163  {
1164  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
1165  return 1;
1166  }
1167 
1168  // Get the axis on which the first rsector is positionned
1169  if(rsector_pos_Y!=0) is_rsector_Y_axis = true;
1170 
1171  // Get the detector ordering depending on the position of the first rsector
1172  // (inverted if located on top (Y-axis with positive value)
1173  // or on the X axis with negative value)
1174 
1175  if(rsector_pos_Y > 0 || rsector_pos_X < 0)
1176  invert_det_order = true;
1177 
1178  }
1179 
1180 
1181  kword = "/gate/"+rsector_name+"/ring/setRepeatNumber";
1182  values = CheckGATECommand(kword, line);
1183  if (values.size()>0)
1184  {
1185  if(ConvertFromString( values[0] , &nRsectorsAngPos) )
1186  {
1187  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1188  return 1;
1189  }
1190  }
1191 
1192 
1193  // Check if (axial) linear repeaters for rsectors
1194  kword = "/gate/"+rsector_name+"/linear/setRepeatNumber";
1195  values = CheckGATECommand(kword, line);
1196  if (values.size()>0)
1197  {
1198  if(ConvertFromString(values[0], &nRsectorsAxial) )
1199  {
1200  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
1201  return 1;
1202  }
1203  }
1204 
1205 
1206  // Check if cubic array repeaters for rsectors
1207  kword = "/gate/"+rsector_name+"/cubicArray/setRepeatNumberZ";
1208  values = CheckGATECommand(kword, line);
1209 
1210  if (values.size()>0)
1211  {
1212  // Using a cubic array after a ring repeater (nRsectorsAngPos>1) leads to rsector being ordered axially first (and not transaxially)
1213  // Track this using a flag
1214  rsector_id_order = nRsectorsAngPos>1 ? 1 : 0 ;
1215 
1216  if(ConvertFromString(values[0], &nRsectorsAxial) )
1217  {
1218  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
1219  return 1;
1220  }
1221  }
1222 
1223 
1224 
1225 
1226  // MODULES
1227 
1228  // Box/cubicArray repeaters
1229  kword = is_rsector_Y_axis ?
1230  "/gate/"+module_name+"/cubicArray/setRepeatNumberX":
1231  "/gate/"+module_name+"/cubicArray/setRepeatNumberY";
1232 
1233  values = CheckGATECommand(kword, line);
1234  if (values.size()>0)
1235  {
1236  if(ConvertFromString( values[0] , &nModulesTransaxial) )
1237  {
1238  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1239  return 1;
1240  }
1241  }
1242 
1243  kword = "/gate/"+module_name+"/cubicArray/setRepeatNumberZ";
1244  values = CheckGATECommand(kword, line);
1245  if (values.size()>0)
1246  {
1247  if(ConvertFromString( values[0] , &nModulesAxial) )
1248  {
1249  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1250  return 1;
1251  }
1252  }
1253 
1254  // linear repeaters instead of box
1255  kword = "/gate/"+module_name+"/linear/setRepeatNumber";
1256  values = CheckGATECommand(kword, line);
1257  if (values.size()>0)
1258  {
1259  if(ConvertFromString( values[0] , &nModulesAxial) )
1260  {
1261  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1262  return 1;
1263  }
1264  }
1265 
1266 
1267  // SUBMODULES
1268 
1269  kword = is_rsector_Y_axis ?
1270  "/gate/"+submodule_name+"/cubicArray/setRepeatNumberX":
1271  "/gate/"+submodule_name+"/cubicArray/setRepeatNumberY";
1272 
1273  values = CheckGATECommand(kword, line);
1274  if (values.size()>0)
1275  {
1276  if(ConvertFromString( values[0] , &nSubmodulesTransaxial) )
1277  {
1278  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1279  return 1;
1280  }
1281  }
1282 
1283  kword = "/gate/"+submodule_name+"/cubicArray/setRepeatNumberZ";
1284  values = CheckGATECommand(kword, line);
1285  if (values.size()>0)
1286  {
1287  if(ConvertFromString( values[0] , &nSubmodulesAxial) )
1288  {
1289  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1290  return 1;
1291  }
1292  }
1293 
1294  // linear repeaters instead of box
1295  kword = "/gate/"+submodule_name+"/linear/setRepeatNumber";
1296  values = CheckGATECommand(kword, line);
1297  if (values.size()>0)
1298  {
1299  if(ConvertFromString( values[0] , &nSubmodulesAxial) )
1300  {
1301  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1302  return 1;
1303  }
1304  }
1305 
1306 
1307 
1308  // CRYSTALS
1309 
1310  kword = is_rsector_Y_axis ?
1311  "/gate/"+crystal_name+"/cubicArray/setRepeatNumberX":
1312  "/gate/"+crystal_name+"/cubicArray/setRepeatNumberY";
1313 
1314  values = CheckGATECommand(kword, line);
1315  if (values.size()>0)
1316  {
1317  if(ConvertFromString( values[0] , &nCrystalsTransaxial) )
1318  {
1319  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1320  return 1;
1321  }
1322  }
1323 
1324  kword = "/gate/"+crystal_name+"/cubicArray/setRepeatNumberZ";
1325  values = CheckGATECommand(kword, line);
1326  if (values.size()>0)
1327  {
1328  if(ConvertFromString( values[0] , &nCrystalsAxial) )
1329  {
1330  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1331  return 1;
1332  }
1333  }
1334 
1335 
1336  // linear repeaters instead of box
1337  kword = "/gate/"+crystal_name+"/linear/setRepeatNumber";
1338  values = CheckGATECommand(kword, line);
1339  if (values.size()>0)
1340  {
1341  if(ConvertFromString( values[0] , &nCrystalsAxial) )
1342  {
1343  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1344  return 1;
1345  }
1346  }
1347 
1348 
1349  // LAYERS
1350 
1351  // Check if there are any repeaters on crystal layers
1352  for(int l=0 ; l<nLayers ; l++)
1353  {
1354  kword = is_rsector_Y_axis ?
1355  "/gate/"+layers_name[l]+"/cubicArray/setRepeatNumberX":
1356  "/gate/"+layers_name[l]+"/cubicArray/setRepeatNumberY";
1357 
1358  values = CheckGATECommand(kword, line);
1359  if (values.size()>0)
1360  {
1361  int32_t val;
1362  if(ConvertFromString( values[0] , &val) )
1363  {
1364  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1365  return 1;
1366  }
1367  nLayersRptTransaxial.push_back(val);
1368  }
1369 
1370  kword = "/gate/"+layers_name[l]+"/cubicArray/setRepeatNumberZ";
1371  values = CheckGATECommand(kword, line);
1372  if (values.size()>0)
1373  {
1374  int32_t val;
1375  if(ConvertFromString( values[0] , &val) )
1376  {
1377  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1378  return 1;
1379  }
1380  nLayersRptAxial.push_back(val);
1381  }
1382 
1383  // linear repeaters instead of box
1384  kword = "/gate/"+layers_name[l]+"/linear/setRepeatNumber";
1385  values = CheckGATECommand(kword, line);
1386  if (values.size()>0)
1387  {
1388  int32_t val;
1389  if(ConvertFromString( values[0] , &val) )
1390  {
1391  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1392  return 1;
1393  }
1394  nLayersRptAxial.push_back(val);
1395  }
1396 
1397 
1398  }
1399 
1400 
1401  kword = "/gate/application/setTimeStart";
1402  values = CheckGATECommand(kword, line);
1403  if (values.size()>0)
1404  {
1405  if(ConvertFromString( values[0] , &time_start) )
1406  {
1407  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1408  return 1;
1409  }
1410 
1411  // Convert in ms
1412  if (values.size()>1)
1413  {
1414  if(values[1] == "s") time_start *= 1000;
1415  }
1416  else
1417  Cerr("***** dataConversionUtilities::readMacCylindrical()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
1418 
1419  start_time_ms = time_start;
1420  }
1421 
1422  kword = "/gate/application/setTimeStop";
1423  values = CheckGATECommand(kword, line);
1424  if (values.size()>0)
1425  {
1426  if(ConvertFromString( values[0] , &time_stop) )
1427  {
1428  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1429  return 1;
1430  }
1431 
1432  // Convert in ms
1433  if (values.size()>1)
1434  {
1435  if(values[1] == "s") time_stop *= 1000;
1436  }
1437  else
1438  Cerr("***** dataConversionUtilities::readMacCylindrical()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
1439  }
1440 
1441  kword = "/gate/application/addSlice";
1442  values = CheckGATECommand(kword, line);
1443  if (values.size()>0)
1444  {
1445  double time_slice_tmp=0;
1446 
1447  if(ConvertFromString( values[0] , &time_slice_tmp) )
1448  {
1449  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1450  return 1;
1451  }
1452 
1453  // Convert in ms
1454  if (values.size()>1)
1455  {
1456  if(values[1] == "s") time_slice_tmp *= 1000;
1457  }
1458  else
1459  Cerr("***** dataConversionUtilities::readMacCylindrical()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
1460 
1461  time_slices += time_slice_tmp;
1462  }
1463 
1464  kword = "/gate/digitizer/Coincidences/setWindow";
1465  values = CheckGATECommand(kword, line);
1466  if (values.size()>0)
1467  {
1468  if(ConvertFromString( values[0] , &pet_coinc_window) )
1469  {
1470  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1471  return 1;
1472  }
1473 
1474  // Convert to ns
1475  if (values.size()>1)
1476  {
1477  if(values[1] == "ns") pet_coinc_window *= 1000.;
1478  else if (values[1] == "us") pet_coinc_window *= 1000000.;
1479  else if (values[1] == "ps") ;
1480  else
1481  {
1482  Cerr("***** dataConversionUtilities::readMacCylindrical()-> ERROR : can't read unit of '"<< kword << ". Must be ns, ps or us !!!");
1483  return 1;
1484  }
1485 
1486  }
1487  else
1488  Cerr("***** dataConversionUtilities::readMacCylindrical()-> WARNING : can't read unit of '"<< kword << ". Assuming time in ns");
1489 
1490  }
1491 
1492  }
1493 
1494  // Compute duration in ms
1495  duration_ms = (time_slices>0) ?
1496  (uint32_t)(time_slices-time_start) :
1497  duration_ms;
1498 
1499  duration_ms = (time_start>=0 && time_stop>=0) ?
1500  (uint32_t)(time_stop-time_start) :
1501  duration_ms ;
1502 
1503  mac_file.close();
1504  }
1505 
1506  // Computing the total number of crystals in the scanner
1507  if(nLayers == 0)
1508  {
1509  nCrystalsTot = nRsectorsAngPos * nRsectorsAxial
1510  * nModulesTransaxial * nModulesAxial
1511  * nSubmodulesTransaxial * nSubmodulesAxial
1512  * nCrystalsTransaxial * nCrystalsAxial;
1513  }
1514  else
1515  for(int l=0 ; l<nLayers ; l++)
1516  {
1517  uint32_t nb_crystals_layer = nRsectorsAngPos * nRsectorsAxial
1518  * nModulesTransaxial * nModulesAxial
1519  * nSubmodulesTransaxial * nSubmodulesAxial
1520  * nCrystalsTransaxial * nCrystalsAxial;
1521 
1522  // Add layer elements if repeaters have been used on layers
1523  if(nLayersRptTransaxial.size()>0 || nLayersRptAxial.size()>0 )
1524  nb_crystals_layer *= nLayersRptTransaxial[l] * nLayersRptAxial[l];
1525 
1526  nb_crystal_per_layer[l] = nb_crystals_layer;
1527 
1528  nCrystalsTot += nb_crystals_layer;
1529  }
1530 
1531 
1532  if(vb >= 2)
1533  {
1534  Cout(endl);
1535  Cout("-----------------------------------------------------------" << endl);
1536  Cout("ReadMacCylindrical()-> Information recovered from mac file:" << endl);
1537  Cout("-----------------------------------------------------------" << endl);
1538  Cout("Number of rsectors angular position: " << nRsectorsAngPos << endl);
1539  Cout("Number of axial rsectors: " << nRsectorsAxial << endl);
1540  Cout("Number of axial modules: " << nModulesAxial << endl);
1541  Cout("Number of transaxial modules: " << nModulesTransaxial << endl);
1542  Cout("Number of axial submodules: " << nSubmodulesAxial << endl);
1543  Cout("Number of transaxial submodules: " << nSubmodulesTransaxial << endl);
1544  Cout("Number of axial crystals: " << nCrystalsAxial << endl);
1545  Cout("Number of transaxial crystals: " << nCrystalsTransaxial << endl);
1546  if (nLayers>=1)
1547  {
1548  Cout("Number of layers: " << (uint16_t)nLayers << endl); // cast to uint16_t for output purposes
1549  for(int l=0 ; l<nLayers ; l++)
1550  Cout("Layer "<< l <<" : Number of crystals: " << nb_crystal_per_layer[l] << endl);
1551  }
1552  Cout("Total number of crystals (including layers): " << nCrystalsTot << endl);
1553  Cout("Acquisition start time (ms): " << start_time_ms << endl);
1554  Cout("Acquisition duration (ms): " << duration_ms << endl);
1555  Cout("-----------------------------------------------------------" << endl << endl);
1556  }
1557 
1558  return 0;
1559 }
1560 
1561 
1562 
1563 // =====================================================================
1564 // ---------------------------------------------------------------------
1565 // ---------------------------------------------------------------------
1566 // =====================================================================
1567 /*
1568  \fn ReadMacECAT
1569  \param a_pathMac : path to a macro file
1570  \param nCrystalsAxial : nb of axial crystals
1571  \param nCrystalsTransaxial : nb of transaxial crystals
1572  \param nBlocksLine : nb of axial blocks
1573  \param nBlocksPerRing : nb of blocks per ring
1574  \param start_time_ms : acquisition start time converted in ms
1575  \param duration_ms : acquisition duration converted in ms
1576  \param pet_coinc_window : coincidence window (required for TOF parameters)
1577  \param vb : verbosity
1578  \brief Recover informations about the scanner element of an ECAT system, and acquisition duration, from a GATE macro file
1579  \return 0 if success, positive value otherwise
1580 */
1581 int ReadMacECAT( string a_pathMac,
1582  uint32_t &nCrystalsTot,
1583  uint32_t &nCrystalsAxial,
1584  uint32_t &nCrystalsTransaxial,
1585  uint32_t &nBlocksLine,
1586  uint32_t &nBlocksPerRing,
1587  uint32_t &start_time_ms,
1588  uint32_t &duration_ms,
1589  FLTNB &pet_coinc_window,
1590  int vb)
1591 {
1592  // Recover path to all macro files from the main mac file
1593  vector<string> path_mac_files;
1594  path_mac_files.push_back(a_pathMac);
1595  if(GetGATEMacFiles(a_pathMac , path_mac_files))
1596  {
1597  Cerr("***** GetGATESystemType() ->Error occurred when trying to recover paths to GATE macro files !" << endl);
1598  return 1;
1599  }
1600 
1601  string block_name = "block";
1602  string crystal_name = "crystal";
1603  bool is_block_Y_axis = false;
1604 
1605  // Recover aliases of the different parts of the architecture
1606  if(GetGATEAliasesEcat(path_mac_files, block_name, crystal_name, vb) )
1607  {
1608  Cerr("***** GetGATESystemType() ->Error occurred when trying to recover aliases for the elements of the ecat !" << endl);
1609  return 1;
1610  }
1611 
1612 
1613  // 2nd loop to recover all other informations
1614  for(uint16_t f=0 ; f<path_mac_files.size() ; f++)
1615  {
1616  ifstream mac_file(path_mac_files[f].c_str(), ios::in);
1617 
1618  string line;
1619  double time_start=-1.,
1620  time_stop=-1.,
1621  time_slices=-1.;
1622 
1623  while(getline(mac_file, line))
1624  {
1625  vector <string> values;
1626  string kword ="";
1627 
1628  kword = "/gate/"+block_name+"/placement/setTranslation";
1629  values = CheckGATECommand(kword, line);
1630  if (values.size()>0)
1631  {
1632  FLTNB block_pos_X=0.,
1633  block_pos_Y=0.;
1634 
1635  if(ConvertFromString(values[0], &block_pos_X) ||
1636  ConvertFromString(values[1], &block_pos_Y) )
1637  {
1638  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
1639  return 1;
1640  }
1641 
1642  // Get the axis on which the first rsector is positionned
1643  if(block_pos_Y!=0) is_block_Y_axis = true;
1644  }
1645 
1646  kword = "/gate/"+block_name+"/ring/setRepeatNumber";
1647  values = CheckGATECommand(kword, line);
1648  if (values.size()>0)
1649  {
1650  if(ConvertFromString( values[0] , &nBlocksPerRing) )
1651  {
1652  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1653  return 1;
1654  }
1655  }
1656 
1657  kword = "/gate/"+block_name+"/linear/setRepeatNumber";
1658  values = CheckGATECommand(kword, line);
1659  if (values.size()>0)
1660  {
1661  if(ConvertFromString( values[0] , &nBlocksLine) )
1662  {
1663  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1664  return 1;
1665  }
1666  }
1667 
1668  kword = is_block_Y_axis ?
1669  "/gate/"+crystal_name+"/cubicArray/setRepeatNumberX":
1670  "/gate/"+crystal_name+"/cubicArray/setRepeatNumberY";
1671 
1672  values = CheckGATECommand(kword, line);
1673  if (values.size()>0)
1674  {
1675  if(ConvertFromString( values[0] , &nCrystalsTransaxial) )
1676  {
1677  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1678  return 1;
1679  }
1680  }
1681 
1682  kword = "/gate/"+crystal_name+"/cubicArray/setRepeatNumberZ";
1683  values = CheckGATECommand(kword, line);
1684  if (values.size()>0)
1685  {
1686  if(ConvertFromString( values[0] , &nCrystalsAxial) )
1687  {
1688  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1689  return 1;
1690  }
1691  }
1692 
1693 
1694  kword = "/gate/application/setTimeStart";
1695  values = CheckGATECommand(kword, line);
1696  if (values.size()>0)
1697  {
1698  if(ConvertFromString( values[0] , &time_start) )
1699  {
1700  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1701  return 1;
1702  }
1703 
1704  // Convert in ms
1705  if (values.size()>1)
1706  {
1707  if(values[1] == "s") time_start *= 1000;
1708  }
1709  else
1710  Cerr("***** dataConversionUtilities::readMacECAT()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
1711 
1712  start_time_ms = time_start;
1713  }
1714 
1715  kword = "/gate/application/setTimeStop";
1716  values = CheckGATECommand(kword, line);
1717  if (values.size()>0)
1718  {
1719  if(ConvertFromString( values[0] , &time_stop) )
1720  {
1721  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1722  return 1;
1723  }
1724 
1725  // Convert in ms
1726  if (values.size()>1)
1727  {
1728  if(values[1] == "s") time_stop *= 1000;
1729  }
1730  else
1731  Cerr("***** dataConversionUtilities::readMacECAT()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
1732  }
1733 
1734  kword = "/gate/application/addSlice";
1735  values = CheckGATECommand(kword, line);
1736  if (values.size()>0)
1737  {
1738  double time_slice_tmp=0;
1739 
1740  if(ConvertFromString( values[0] , &time_slice_tmp) )
1741  {
1742  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1743  return 1;
1744  }
1745 
1746  // Convert in ms
1747  if (values.size()>1)
1748  {
1749  if(values[1] == "s") time_slice_tmp *= 1000;
1750  }
1751  else
1752  Cerr("***** dataConversionUtilities::readMacECAT()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
1753 
1754  time_slices = time_slice_tmp;
1755  }
1756 
1757  kword = "/gate/digitizer/Coincidences/setWindow";
1758  values = CheckGATECommand(kword, line);
1759  if (values.size()>0)
1760  {
1761  if(ConvertFromString( values[0] , &pet_coinc_window) )
1762  {
1763  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1764  return 1;
1765  }
1766 
1767  // Convert to ns
1768  if (values.size()>1)
1769  {
1770  if(values[1] == "ns") pet_coinc_window *= 1000.;
1771  else if (values[1] == "us") pet_coinc_window *= 1000000.;
1772  else if (values[1] == "ps") ;
1773  else
1774  {
1775  Cerr("***** dataConversionUtilities::readMacCylindrical()-> ERROR : can't read unit of '"<< kword << ". Must be ns, ps or us !!!");
1776  return 1;
1777  }
1778 
1779  }
1780  else
1781  Cerr("***** dataConversionUtilities::readMacCylindrical()-> WARNING : can't read unit of '"<< kword << ". Assuming time in ns");
1782 
1783  }
1784  }
1785 
1786  // Compute duration in ms
1787  duration_ms = (time_slices>0) ?
1788  (uint32_t)(time_slices-time_start) :
1789  duration_ms;
1790 
1791  duration_ms = (time_start>=0 && time_stop>=0) ?
1792  (uint32_t)(time_stop-time_start) :
1793  duration_ms ;
1794 
1795  mac_file.close();
1796  }
1797 
1798  // Computing the total number of crystals in the scanner
1799  nCrystalsTot = nCrystalsTransaxial * nCrystalsAxial
1800  * nBlocksLine * nBlocksPerRing;
1801 
1802 
1803  if(vb >= 2)
1804  {
1805  Cout(endl);
1806  Cout("-----------------------------------------------------" << endl);
1807  Cout("ReadMacECAT()-> Information recovered from mac file:" << endl);
1808  Cout("-----------------------------------------------------" << endl);
1809  Cout("Number of blocks per ring: " << nBlocksPerRing << endl);
1810  Cout("Number of axial blocks: " << nBlocksLine << endl);
1811  Cout("Number of axial crystals: " << nCrystalsAxial << endl);
1812  Cout("Number of transaxial crystals: " << nCrystalsTransaxial << endl);
1813  Cout("Total number of crystals: " << nCrystalsTot << endl);
1814  Cout("Acquisition start time (ms): " << start_time_ms << endl);
1815  Cout("Acquisition duration (ms): " << duration_ms << endl);
1816  Cout("-----------------------------------------------------" << endl << endl);
1817  }
1818 
1819  return 0;
1820 }
1821 
1822 
1823 
1824 // =====================================================================
1825 // ---------------------------------------------------------------------
1826 // ---------------------------------------------------------------------
1827 // =====================================================================
1828 /*
1829  \fn ReadMacSPECT
1830  \param a_pathMac : path to a macro file
1831  \param distToDetector : distance between center of rotation and detector surface
1832  \param nHeads : nb of cameras
1833  \param nPixAxl : nb of transaxial pixels
1834  \param nPixTrs : nb of axial pixels
1835  \param crystalSizeAxl : crystal axial dimension
1836  \param crystalSizeTrs : crystal transaxial dimension
1837  \param head1stAngle : head first angle
1838  \param headAngPitch : angular pitch between heads
1839  \param headRotSpeed : angle between projection
1840  \param headRotDirection : rotation direction of the head (CW or CCW)
1841  \param start_time_ms : acquisition start time converted in ms
1842  \param duration_ms : acquisition duration converted in ms
1843  \param vb : verbosity
1844  \brief Recover informations about the scanner element of an ECAT system, and acquisition duration, from a GATE macro file
1845  \return 0 if success, positive value otherwise
1846 */
1847 int ReadMacSPECT( string a_pathMac,
1848  float_t &a_distToDetector,
1849  uint32_t &a_nHeads,
1850  uint32_t &a_nPixAxl,
1851  uint32_t &a_nPixTrs,
1852  float_t &a_crystalSizeAxl,
1853  float_t &a_crystalSizeTrs,
1854  uint32_t &a_nProjectionsTot,
1855  uint32_t &a_nProjectionsByHead,
1856  float_t &a_head1stAngle,
1857  float_t &a_headAngPitch,
1858  float_t &a_headAngStepDeg,
1859  int &a_headRotDirection,
1860  uint32_t &a_start_time_ms,
1861  uint32_t &a_duration_ms,
1862  int vb)
1863 {
1864  // Recover path to all macro files from the main mac file
1865  vector<string> path_mac_files;
1866  path_mac_files.push_back(a_pathMac);
1867 
1868  if(GetGATEMacFiles(a_pathMac , path_mac_files))
1869  {
1870  Cerr("***** GetGATESystemType() ->Error occurred when trying to recover paths to GATE macro files !" << endl);
1871  return 1;
1872  }
1873 
1874  string head_name = "SPECThead";
1875  string crystal_name = "crystal";
1876  string pixel_name = "pixel";
1877  string head_orbit_name = "";
1878  bool is_head_Y_axis = false;
1879 
1880  // Recover aliases of the different parts of the architecture
1881  if(GetGATEAliasesSPECT(path_mac_files, head_name, crystal_name, pixel_name, vb) )
1882  {
1883  Cerr("***** GetGATESystemType() ->Error occurred when trying to recover aliases for the elements of the ecat !" << endl);
1884  return 1;
1885  }
1886 
1887  // Init variables to recover some data
1888  double time_start=-1.,
1889  time_stop=-1.,
1890  time_slice=-1,
1891  time_slices=-1.,
1892  time_slice_ms=-1.,
1893  head_rot_speed =-1.;
1894 
1895  // 2nd loop to recover all other informations
1896  for(uint16_t f=0 ; f<path_mac_files.size() ; f++)
1897  {
1898  ifstream mac_file(path_mac_files[f].c_str(), ios::in);
1899 
1900  string line;
1901 
1902  while(getline(mac_file, line))
1903  {
1904  vector <string> values;
1905  string kword ="";
1906  kword = "/gate/"+head_name+"/placement/setTranslation";
1907  values = CheckGATECommand(kword, line);
1908  if (values.size()>0)
1909  {
1910  FLTNB head_pos_X=0.,
1911  head_pos_Y=0.;
1912 
1913  if(ConvertFromString(values[0], &head_pos_X) ||
1914  ConvertFromString(values[1], &head_pos_Y) )
1915  {
1916  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
1917  return 1;
1918  }
1919 
1920  // Get the axis on which the first rsector is positionned
1921  if(head_pos_Y!=0) is_head_Y_axis = true;
1922 
1923  a_distToDetector = is_head_Y_axis ? abs(head_pos_Y) : abs(head_pos_X) ;
1924  }
1925 
1926 
1927 
1928  kword = "/gate/"+head_name+"/ring/setRepeatNumber";
1929  values = CheckGATECommand(kword, line);
1930  if (values.size()>0)
1931  {
1932  if(ConvertFromString(values[0], &a_nHeads))
1933  {
1934  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
1935  return 1;
1936  }
1937  }
1938 
1939  kword = "/gate/"+head_name+"/ring/setFirstAngle";
1940  values = CheckGATECommand(kword, line);
1941  if (values.size()>0)
1942  {
1943  if(ConvertFromString(values[0], &a_head1stAngle))
1944  {
1945  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
1946  return 1;
1947  }
1948  }
1949 
1950  kword = "/gate/"+head_name+"/ring/setAngularPitch";
1951  values = CheckGATECommand(kword, line);
1952  if (values.size()>0)
1953  {
1954  if(ConvertFromString(values[0], &a_headAngPitch))
1955  {
1956  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
1957  return 1;
1958  }
1959  }
1960 
1961  kword = "/gate/"+head_name+"/moves/insert";
1962  values = CheckGATECommand(kword, line);
1963  if (values.size()>0)
1964  {
1965  if(ConvertFromString(values[0], &head_orbit_name))
1966  {
1967  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
1968  return 1;
1969  }
1970  }
1971 
1972  kword = "/gate/"+head_name+"/"+head_orbit_name+"/setSpeed";
1973  values = CheckGATECommand(kword, line);
1974  if (values.size()>0)
1975  {
1976  if(ConvertFromString(values[0], &head_rot_speed))
1977  {
1978  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
1979  return 1;
1980  }
1981  }
1982 
1983  kword = "/gate/"+head_name+"/"+head_orbit_name+"/setPoint2";
1984  values = CheckGATECommand(kword, line);
1985  if (values.size()>0)
1986  {
1987  int rot_direction;
1988  if(ConvertFromString(values[2], &rot_direction))
1989  {
1990  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
1991  return 1;
1992  }
1993 
1994  a_headRotDirection = rot_direction>0 ? GEO_ROT_CW : GEO_ROT_CCW;
1995  }
1996 
1997  // --- Crystals ---
1998  kword = is_head_Y_axis ?
1999  "/gate/"+crystal_name+"/geometry/setXLength":
2000  "/gate/"+crystal_name+"/geometry/setYLength";
2001 
2002  values = CheckGATECommand(kword, line);
2003  if (values.size()>0)
2004  {
2005  if(ConvertFromString(values[0], &a_crystalSizeTrs) )
2006  {
2007  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2008  return 1;
2009  }
2010 
2011  }
2012 
2013  kword = "/gate/"+crystal_name+"/geometry/setZLength";
2014  values = CheckGATECommand(kword, line);
2015  if (values.size()>0)
2016  {
2017  if(ConvertFromString(values[0], &a_crystalSizeAxl) )
2018  {
2019  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2020  return 1;
2021  }
2022  }
2023 
2024 
2025  // --- Pixels ---
2026  kword = is_head_Y_axis ?
2027  "/gate/"+pixel_name+"/cubicArray/setRepeatNumberX":
2028  "/gate/"+pixel_name+"/cubicArray/setRepeatNumberY";
2029 
2030 
2031  values = CheckGATECommand(kword, line);
2032  if (values.size()>0)
2033  {
2034  if(ConvertFromString(values[0], &a_nPixTrs) )
2035  {
2036  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2037  return 1;
2038  }
2039  }
2040 
2041  kword = "/gate/"+pixel_name+"/cubicArray/setRepeatNumberZ";
2042  values = CheckGATECommand(kword, line);
2043  if (values.size()>0)
2044  {
2045  if(ConvertFromString(values[0], &a_nPixAxl) )
2046  {
2047  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2048  return 1;
2049  }
2050  }
2051 
2052  kword = "/gate/application/setTimeStart";
2053  values = CheckGATECommand(kword, line);
2054  if (values.size()>0)
2055  {
2056  if(ConvertFromString( values[0] , &time_start) )
2057  {
2058  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
2059  return 1;
2060  }
2061 
2062  // Convert in ms
2063  if (values.size()>1)
2064  {
2065  if(values[1] == "s") time_start *= 1000;
2066  }
2067  else
2068  Cerr("***** dataConversionUtilities::readMacECAT()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
2069 
2070  a_start_time_ms = time_start;
2071  }
2072 
2073  kword = "/gate/application/setTimeSlice";
2074  values = CheckGATECommand(kword, line);
2075  if (values.size()>0)
2076  {
2077  if(ConvertFromString( values[0] , &time_slice) )
2078  {
2079  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
2080  return 1;
2081  }
2082 
2083  // Convert in ms
2084  if (values.size()>1)
2085  {
2086  if(values[1] == "s") time_slice *= 1000;
2087  }
2088  else
2089  Cerr("***** dataConversionUtilities::readMacECAT()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
2090 
2091  time_slice_ms = time_slice;
2092  }
2093 
2094  kword = "/gate/application/setTimeStop";
2095  values = CheckGATECommand(kword, line);
2096  if (values.size()>0)
2097  {
2098  if(ConvertFromString( values[0] , &time_stop) )
2099  {
2100  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
2101  return 1;
2102  }
2103 
2104  // Convert in ms
2105  if (values.size()>1)
2106  {
2107  if(values[1] == "s") time_stop *= 1000;
2108  }
2109  else
2110  Cerr("***** dataConversionUtilities::readMacECAT()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
2111  }
2112 
2113  kword = "/gate/application/addSlice";
2114  values = CheckGATECommand(kword, line);
2115  if (values.size()>0)
2116  {
2117  double time_slice_tmp=0;
2118 
2119  if(ConvertFromString( values[0] , &time_slice_tmp) )
2120  {
2121  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
2122  return 1;
2123  }
2124 
2125  // Convert in ms
2126  if (values.size()>1)
2127  {
2128  if(values[1] == "s") time_slice_tmp *= 1000;
2129  }
2130  else
2131  Cerr("***** dataConversionUtilities::readMacECAT()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
2132 
2133  time_slices = time_slice_tmp;
2134  }
2135  }
2136 
2137  // Compute duration in ms
2138 
2139  // Time slices were provided
2140  a_duration_ms = (time_slices>0) ?
2141  (uint32_t)(time_slices-time_start) :
2142  a_duration_ms;
2143 
2144  // Time stop/start is provided
2145  a_duration_ms = (time_start>=0 && time_stop>=0) ?
2146  (uint32_t)(time_stop-time_start) :
2147  a_duration_ms;
2148 
2149 
2150  // Compute the nb of projections and total number of crystals in the system
2151  a_nProjectionsByHead = a_duration_ms / time_slice_ms;
2152  a_nProjectionsTot = a_nHeads*a_nProjectionsByHead;
2153 
2154  // Compute angular pitch if not provided in the mac files (=-1)
2155  a_headAngPitch = (a_headAngPitch<0) ?
2156  360./a_nHeads :
2157  a_headAngPitch ;
2158 
2159  a_headAngStepDeg = head_rot_speed*time_slice_ms/1000.;
2160 
2161  if(head_rot_speed<0)
2162  {
2163  Cerr("***** GetGATESystemType() -> Error couldn't find line '/gate/"+head_name+"/"+head_orbit_name+"/setSpeed' !" << endl);
2164  Cerr(" This information is mandatory to compute the projection angle step." << endl);
2165  return 1;
2166  }
2167 
2168  if(time_slice_ms<0)
2169  {
2170  Cerr("***** GetGATESystemType() -> Error couldn't find line '/gate/application/setTimeSlice' !" << endl);
2171  Cerr(" This information is mandatory to compute the projection angle step." << endl);
2172  return 1;
2173  }
2174 
2175  if(a_duration_ms == 0)
2176  {
2177  if(time_stop <0)
2178  {
2179  Cerr("***** GetGATESystemType() -> Error couldn't compute acquisition find line '/gate/application/setTimeStop' !" << endl);
2180  Cerr(" This information is mandatory to compute the acquisition duration." << endl);
2181  return 1;
2182  }
2183  if(time_start <0)
2184  {
2185  Cerr("***** GetGATESystemType() -> Error couldn't compute acquisition find line '/gate/application/setTimeStart' !" << endl);
2186  Cerr(" This information is mandatory to compute the acquisition duration." << endl);
2187  return 1;
2188  }
2189  }
2190 
2191  mac_file.close();
2192  } // end loop on .mac files
2193 
2194  if(vb >= 2)
2195  {
2196  Cout(endl);
2197  Cout("-----------------------------------------------------" << endl);
2198  Cout("ReadMacSPECT()-> Information recovered from mac file:" << endl);
2199  Cout("-----------------------------------------------------" << endl);
2200  Cout("Distance to detector: " << a_distToDetector << endl);
2201  Cout("Number of heads: " << a_nHeads << endl);
2202  Cout("Number of axial pixels: " << a_nPixAxl << endl);
2203  Cout("Number of transaxial pixels: " << a_nPixTrs << endl);
2204  Cout("Crystal axial size: " << a_crystalSizeAxl << endl);
2205  Cout("Crystal transaxial size: " << a_crystalSizeTrs << endl);
2206  Cout("Number of projections per head: " << a_nProjectionsByHead << endl);
2207  Cout("Total number of projections: " << a_nProjectionsTot << endl);
2208  Cout("Head(s) first transaxial angle: " << a_head1stAngle << endl);
2209  Cout("Head(s) angular pitch: " << a_headAngPitch << endl);
2210  Cout("Angular step between projections (deg): " << a_headAngStepDeg << endl);
2211  Cout("Rotation direction (0=CW, 1=CCW): " << a_headRotDirection << endl);
2212  Cout("Acquisition start time (ms): " << a_start_time_ms << endl);
2213  Cout("Acquisition duration (ms): " << a_duration_ms << endl);
2214  Cout("-----------------------------------------------------" << endl << endl);
2215  }
2216 
2217  return 0;
2218 }
2219 
2220 
2221 
2222 
2223 // =====================================================================
2224 // ---------------------------------------------------------------------
2225 // ---------------------------------------------------------------------
2226 // =====================================================================
2227 /*
2228  \fn ReadIntfSPECT
2229  \param a_pathIntf : path to the interfile header
2230  \param distToDetector : distance between center of rotation and detector surface
2231  \param nHeads : nb of cameras
2232  \param nPixAxl : nb of transaxial pixels
2233  \param nPixTrs : nb of axial pixels
2234  \param crystalSizeAxl : crystal axial dimension
2235  \param crystalSizeTrs : crystal transaxial dimension
2236  \param head1stAngle : head first angle
2237  \param headAngPitch : angular pitch between heads
2238  \param headRotSpeed : angle between projection
2239  \param headRotDirection : rotation direction of the head (CW or CCW)
2240  \param start_time_ms : acquisition start time converted in ms
2241  \param duration_ms : acquisition duration converted in ms
2242  \param vb : verbosity
2243  \brief Recover informations about the scanner element of an ECAT system, and acquisition duration, from a GATE macro file
2244  \return 0 if success, positive value otherwise
2245 */
2246 int ReadIntfSPECT(string a_pathIntf,
2247  float_t &a_distToDetector,
2248  uint32_t &a_nHeads,
2249  uint32_t &a_nPixAxl,
2250  uint32_t &a_nPixTrs,
2251  float_t &a_crystalSizeAxl,
2252  float_t &a_crystalSizeTrs,
2253  uint32_t &a_nProjectionsTot,
2254  uint32_t &a_nProjectionsByHead,
2255  float_t &a_head1stAngle,
2256  float_t &a_headAngPitchDeg,
2257  float_t &a_headAngStepDeg,
2258  int &a_headRotDirection,
2259  uint32_t &a_start_time_ms,
2260  uint32_t &a_duration_ms,
2261  int vb)
2262 {
2263  // Read all required information in Interfile header
2264 
2265  string key;
2266 
2267  key = "matrix size [1]";
2268  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &a_nPixTrs, 1, KEYWORD_MANDATORY))
2269  {
2270  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2271  Cerr(" Either key not found or conversion error" << endl);
2272  return 1;
2273  }
2274 
2275  key = "matrix size [2]";
2276  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &a_nPixAxl, 1, KEYWORD_MANDATORY))
2277  {
2278  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2279  Cerr(" Either key not found or conversion error" << endl);
2280  return 1;
2281  }
2282 
2283  FLTNB size_pix_trs = 1.,
2284  size_pix_axl = 1.;
2285 
2286  key = "scaling factor (mm/pixel) [1]";
2287  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &size_pix_trs, 1, KEYWORD_MANDATORY))
2288  {
2289  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2290  Cerr(" Either key not found or conversion error" << endl);
2291  return 1;
2292  }
2293 
2294  key = "scaling factor (mm/pixel) [2]";
2295  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &size_pix_axl, 1, KEYWORD_MANDATORY))
2296  {
2297  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2298  Cerr(" Either key not found or conversion error" << endl);
2299  return 1;
2300  }
2301 
2302  key = "total number of images";
2303  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &a_nProjectionsTot, 1, KEYWORD_MANDATORY))
2304  {
2305  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2306  Cerr(" Either key not found or conversion error" << endl);
2307  return 1;
2308  }
2309 
2310  key = "number of projections";
2311  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &a_nProjectionsByHead, 1, KEYWORD_MANDATORY))
2312  {
2313  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2314  Cerr(" Either key not found or conversion error" << endl);
2315  return 1;
2316  }
2317 
2318  key = "number of detector heads";
2319  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &a_nHeads, 1, KEYWORD_MANDATORY))
2320  {
2321  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2322  Cerr(" Either key not found or conversion error" << endl);
2323  return 1;
2324  }
2325 
2326  key = "study duration (sec)";
2327  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &a_duration_ms, 1, KEYWORD_MANDATORY))
2328  {
2329  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2330  Cerr(" Either key not found or conversion error" << endl);
2331  return 1;
2332  }
2333  // convert duration in ms
2334  a_duration_ms *= 1000;
2335 
2336 
2337  FLTNB size_crystal_X =0.,
2338  size_crystal_Y =0.;
2339 
2340  key = "crystal x dimension (cm)";
2341  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &size_crystal_X, 1, KEYWORD_OPTIONAL) == 1)
2342  {
2343  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2344  Cerr(" Either key not found or conversion error" << endl);
2345  return 1;
2346  }
2347 
2348  key = "crystal y dimension (cm)";
2349  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &size_crystal_Y, 1, KEYWORD_OPTIONAL) == 1)
2350  {
2351  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2352  Cerr(" Either key not found or conversion error" << endl);
2353  return 1;
2354  }
2355 
2356  key = "crystal z dimension (cm)";
2357  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &a_crystalSizeAxl, 1, KEYWORD_OPTIONAL) == 1)
2358  {
2359  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2360  Cerr(" Either key not found or conversion error" << endl);
2361  return 1;
2362  }
2363 
2364  // Just pick the larger value to get the transaxial crystal size
2365  // convert in mm (values in cm in Interfile)
2366  if(size_crystal_X>0 && size_crystal_Y>0)
2367  a_crystalSizeTrs = (size_crystal_X>size_crystal_Y) ? size_crystal_X*10. : size_crystal_Y*10.;
2368  a_crystalSizeAxl = (a_crystalSizeAxl>0) ? a_crystalSizeAxl*10. : a_crystalSizeAxl ;
2369 
2370 
2371  FLTNB head_pos_X =-1.,
2372  head_pos_Y =-1.,
2373  head_pos_Z =-1.;
2374 
2375  key = "head x translation (cm)";
2376  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &head_pos_X, 1, KEYWORD_OPTIONAL) == 1)
2377  {
2378  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2379  Cerr(" Either key not found or conversion error" << endl);
2380  return 1;
2381  }
2382 
2383  key = "head y translation (cm)";
2384  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &head_pos_Y, 1, KEYWORD_OPTIONAL) == 1)
2385  {
2386  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2387  Cerr(" Either key not found or conversion error" << endl);
2388  return 1;
2389  }
2390 
2391  key = "head z translation (cm)";
2392  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &head_pos_Z, 1, KEYWORD_OPTIONAL) == 1)
2393  {
2394  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2395  Cerr(" Either key not found or conversion error" << endl);
2396  return 1;
2397  }
2398 
2399  // Pick the largest value to initialize distance betweeen COR and detector, converted in mm (cm by default)
2400  if(head_pos_X > head_pos_Y)
2401  a_distToDetector = head_pos_X > head_pos_Z ? head_pos_X*10 : head_pos_Z*10;
2402  else
2403  a_distToDetector = head_pos_Y > head_pos_Z ? head_pos_Y*10 : head_pos_Z*10;
2404 
2405 
2406  key = "direction of rotation";
2407  string rot_dir;
2408  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &rot_dir, 1, KEYWORD_MANDATORY))
2409  {
2410  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2411  Cerr(" Either key not found or conversion error" << endl);
2412  return 1;
2413  }
2414 
2415  a_headRotDirection = (rot_dir == "CCW") ? GEO_ROT_CCW : GEO_ROT_CW ;
2416 
2417  // Could have several keys with this name (each for each head)
2418  // It will recover the value corresponding to the first match
2419  key = "start angle";
2420  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &a_head1stAngle, 1, KEYWORD_MANDATORY))
2421  {
2422  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2423  Cerr(" Either key not found or conversion error" << endl);
2424  return 1;
2425  }
2426 
2427  key = "extent of rotation";
2428  uint32_t extent_rotation =360;
2429  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &extent_rotation, 1, KEYWORD_MANDATORY))
2430  {
2431  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2432  Cerr(" Either key not found or conversion error" << endl);
2433  return 1;
2434  }
2435 
2436  a_headAngStepDeg = (FLTNB)extent_rotation / a_nProjectionsByHead;
2437 
2438  float_t first_angle = 0;
2439  float_t second_angle = extent_rotation;
2440 
2441  key = "start angle";
2442  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &first_angle, 1, KEYWORD_MANDATORY))
2443  {
2444  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2445  Cerr(" Either key not found or conversion error" << endl);
2446  return 1;
2447  }
2448 
2449  key = "start angle";
2450  if(IntfKeyGetRecurringValueFromFile(a_pathIntf.c_str(), key.c_str(), &second_angle, 1, KEYWORD_MANDATORY, 1))
2451  {
2452  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2453  Cerr(" Either key not found or conversion error" << endl);
2454  return 1;
2455  }
2456 
2457  a_headAngPitchDeg = second_angle - first_angle;
2458 
2459  return 0;
2460 }
2461 
2462 
2463 
2464 
2465 // =====================================================================
2466 // ---------------------------------------------------------------------
2467 // ---------------------------------------------------------------------
2468 // =====================================================================
2469 /*
2470  \fn CreateGeomWithCylindrical()
2471  \param a_pathMac : string containing the path to a GATE macro file
2472  \param a_pathGeom : string containing the path to a CASToR output geom file
2473  \brief Read a GATE macro file containing the description of a cylindricalPET system, and convert it to a geom file
2474  \return 0 if success, positive value otherwise
2475 */
2476 int CreateGeomWithCylindrical(string a_pathMac, string a_pathGeom)
2477 {
2478  /* Declaring variables */
2479 
2480  // Scanner
2481  string modality;
2482  string scanner_name = GetFileFromPath(a_pathGeom.substr(0,a_pathGeom.find(".geom")));
2483  double scanner_radius = 0.;
2484  uint32_t number_of_elements = 0;
2485  string description = "PET system extracted from GATE macro: " + a_pathMac;
2486 
2487  // Rsectors
2488  string rsector_name = "";
2489  uint32_t number_of_rsectors_ang = 1; // repeater on a ring
2490  uint32_t number_of_rsectors_axl = 1; // linear repeater
2491  double rsector_step_axl = 0.;
2492  double rsector_gap_axl = 0.;
2493  double rsector_first_angle = 0.;
2494  double rsector_angular_span = 360.;
2495  double rsector_pos_X = 0.;
2496  double rsector_pos_Y = 0.;
2497  uint32_t rsector_nb_zshifts = 0;
2498  vector <double> vec_rsector_Z_Shift;
2499  bool is_rsector_Y_axis = false;
2500  double rsector_size_trs;
2501  double rsector_size_axl;
2502 
2503  // Modules
2504  string module_name = "";
2505  uint32_t number_of_modules_trs = 1;
2506  uint32_t number_of_modules_axl = 1;
2507  double module_step_trs = 0.;
2508  double module_step_axl = 0.;
2509  double module_gap_trs = 0.;
2510  double module_gap_axl = 0.;
2511  double module_size_trs;
2512  double module_size_axl;
2513 
2514  // Submodules
2515  string submodule_name = "";
2516  uint32_t number_of_submodules_trs = 1;
2517  uint32_t number_of_submodules_axl = 1;
2518  double submodule_step_trs = 0.;
2519  double submodule_step_axl = 0.;
2520  double submodule_gap_trs = 0.;
2521  double submodule_gap_axl = 0.;
2522  double submodule_size_trs;
2523  double submodule_size_axl;
2524 
2525  // Crystals
2526  string crystal_name = "";
2527  uint32_t number_of_crystals_trs = 1;
2528  uint32_t number_of_crystals_axl = 1;
2529  double crystal_step_trs = 0.;
2530  double crystal_step_axl = 0.;
2531  double crystal_gap_trs = 0.;
2532  double crystal_gap_axl = 0.;
2533  double crystal_size_depth = 0.;
2534  double crystal_size_trs = 0.;
2535  double crystal_size_axl = 0.;
2536 
2537 
2538  // Layers
2539  int number_of_layers = 0;
2540  string n_layers = "1"; //default value for number_of_layers
2541  vector <uint32_t> number_of_lyr_elts_trs;
2542  vector <uint32_t> number_of_lyr_elts_axl;
2543 
2544  vector <string> layers_names;
2545  vector <vector <double> > layers_positions;
2546  vector <double> layers_size_depth;
2547  vector <double> layers_size_trs;
2548  vector <double> layers_size_axl;
2549 
2550  vector <double> layers_step_trs;
2551  vector <double> layers_step_axl;
2552 
2553  // Optional
2554  uint32_t voxels_number_trs;
2555  uint32_t voxels_number_axl;
2556  double fov_trs,
2557  fov_axl;
2558  double mean_depth_of_interaction = -1.;
2559  double min_angle_diff = 0.;
2560 
2561 
2562 
2563  // If we have multiple layers, we need to enter multiple values in the .geom.
2564  vector <string> vec_scanner_radius;
2565 
2566  // Rsector vectors
2567  vector <string> vec_number_of_rsectors_ang;
2568  vector <string> vec_number_of_rsectors_axl;
2569  vector <string> vec_rsector_gap_trs;
2570  vector <string> vec_rsector_gap_axl;
2571  vector <string> vec_rsector_first_angle;
2572 
2573 
2574  // Module vectors
2575  vector <string> vec_number_of_modules_trs;
2576  vector <string> vec_number_of_modules_axl;
2577  vector <string> vec_module_gap_trs;
2578  vector <string> vec_module_gap_axl;
2579 
2580  // Submodule vectors
2581  vector <string> vec_number_of_submodules_trs;
2582  vector <string> vec_number_of_submodules_axl;
2583  vector <string> vec_submodule_gap_trs;
2584  vector <string> vec_submodule_gap_axl;
2585 
2586  // Crystal vectors
2587  vector <string> vec_number_of_crystals_trs;
2588  vector <string> vec_number_of_crystals_axl;
2589  vector <string> vec_crystal_gap_trs;
2590  vector <string> vec_crystal_gap_axl;
2591 
2592  // Optionnal
2593  vector <string> vec_mean_depth_of_interaction;
2594  vector <string> vec_min_angle_diff;
2595 
2596  vector<string> path_mac_files;
2597  path_mac_files.push_back(a_pathMac);
2598 
2599  // Recover path to all macro files from the main mac file
2600  if(GetGATEMacFiles(a_pathMac , path_mac_files))
2601  {
2602  Cerr("***** GetGATESystemType() ->Error occurred when trying to recover paths to GATE macro files !" << endl);
2603  return 1;
2604  }
2605 
2606  // Recover aliases of the different parts of the architecture
2607  if(GetGATEAliasesCylindrical(path_mac_files, rsector_name, module_name, submodule_name, crystal_name, layers_names, 2) )
2608  {
2609  Cerr("***** GetGATESystemType() ->Error occurred when trying to recover aliases for the elements of the cylindricalPET !" << endl);
2610  return 1;
2611  }
2612 
2613  // Recover nb of detected layers
2614  n_layers = layers_names.size();
2615 
2616 
2617  // Loop to recover all other informations
2618  for(uint16_t f=0 ; f<path_mac_files.size() ; f++)
2619  {
2620  ifstream systemMac(path_mac_files[f].c_str(), ios::in);
2621 
2622  // Reading the .mac file line by line and update the variables if a matching adress is found
2623  string line;
2624  while(getline(systemMac, line))
2625  {
2626  vector <string> values;
2627 
2628  // Scanner
2629  modality = "PET";
2630 
2631  string entry = "";
2632 
2633  entry = "/gate/"+rsector_name+"/placement/setTranslation";
2634  values = CheckGATECommand(entry, line);
2635 
2636  // Assumes that first rsector located on the X or Y axis
2637  if (values.size()>0)
2638  {
2639  if(ConvertFromString(values[0], &rsector_pos_X) ||
2640  ConvertFromString(values[1], &rsector_pos_Y) )
2641  {
2642  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2643  return 1;
2644  }
2645 
2646  // Check first rsector cartesian coordinates is 0 on X or Y axis
2647  // Throw error otherwise
2648  if(rsector_pos_X!=0 && rsector_pos_Y !=0)
2649  {
2650  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2651  Cerr(" Rsector cartesian coordinates on either the X or Y axis expected to be equal to 0 "<< endl);
2652  return 1;
2653  }
2654 
2655  // Get the axis on which the first rsector is positionned
2656  if(rsector_pos_Y!=0) is_rsector_Y_axis = true;
2657 
2658  // Adjust scanner radius to the center position of the rsector
2659  scanner_radius += is_rsector_Y_axis ? abs(rsector_pos_Y) : abs(rsector_pos_X) ;
2660  }
2661 
2662  // Old version accounting on the rsector depth to be equal to the detector depth
2663  // in order to get the position of the detector surface.
2664  // Current version tracks the 'setTranslation' commands to get to the actual detector surface position
2665  /*
2666  // Adjust scanner radius (from center to rsector surface) according to block size
2667  entry = is_rsector_Y_axis ?
2668  "/gate/"+rsector_name+"/geometry/setYLength" :
2669  "/gate/"+rsector_name+"/geometry/setXLength";
2670 
2671  values = CheckGATECommand(entry, line);
2672  if (values.size()>0)
2673  {
2674  double rsector_size_depth = 0.;
2675  if(ConvertFromString(values[0], &rsector_size_depth) )
2676  {
2677  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2678  return 1;
2679  }
2680 
2681  scanner_radius -= rsector_size_depth/2;
2682  }
2683  */
2684 
2685 
2686  // rsector transaxial size
2687  entry = is_rsector_Y_axis ?
2688  "/gate/"+rsector_name+"/geometry/setXLength":
2689  "/gate/"+rsector_name+"/geometry/setYLength";
2690 
2691  values = CheckGATECommand(entry, line);
2692  if (values.size()>0)
2693  {
2694  if(ConvertFromString(values[0], &rsector_size_trs) )
2695  {
2696  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2697  return 1;
2698  }
2699  }
2700 
2701 
2702 
2703  // --- Rsectors ---
2704 
2705  entry = "/gate/"+rsector_name+"/ring/setModuloNumber";
2706  values = CheckGATECommand(entry, line);
2707  if (values.size()>0)
2708  {
2709  if(ConvertFromString(values[0], &rsector_nb_zshifts) )
2710  {
2711  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2712  return 1;
2713  }
2714  }
2715 
2716  entry = "/gate/"+rsector_name+"/ring/setRepeatNumber";
2717  values = CheckGATECommand(entry, line);
2718  if (values.size()>0)
2719  {
2720  if(ConvertFromString(values[0], &number_of_rsectors_ang) )
2721  {
2722  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2723  return 1;
2724  }
2725  }
2726 
2727 
2728 
2729  // Check if (axial) linear repeaters for rsectors
2730  entry = "/gate/"+rsector_name+"/linear/setRepeatNumber";
2731  values = CheckGATECommand(entry, line);
2732  if (values.size()>0)
2733  {
2734  if(ConvertFromString(values[0], &number_of_rsectors_axl) )
2735  {
2736  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2737  return 1;
2738  }
2739  }
2740 
2741 
2742 
2743 
2744  entry = "/gate/"+rsector_name+"/linear/setRepeatVector";
2745  values = CheckGATECommand(entry, line);
2746  if (values.size()>0)
2747  {
2748  if(ConvertFromString(values[2], &rsector_step_axl) )
2749  {
2750  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2751  return 1;
2752  }
2753  }
2754 
2755 
2756  // Check if cubic array repeaters for rsectors
2757  entry = is_rsector_Y_axis ?
2758  "/gate/"+rsector_name+"/cubicArray/setRepeatNumberX":
2759  "/gate/"+rsector_name+"/cubicArray/setRepeatNumberY";
2760 
2761  values = CheckGATECommand(entry, line);
2762  if (values.size()>0)
2763  {
2764  uint32_t number_of_rsectors_trs;
2765 
2766  if(ConvertFromString(values[0], &number_of_rsectors_trs) )
2767  {
2768  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2769  return 1;
2770  }
2771 
2772  // Check if more than one transaxial rsector is present.
2773  // Not currently implemented as the geometry model can vary according to whether a ring repeater is inserted before or after the cubicarray repeater
2774  // Throw error in this situation
2775  if(number_of_rsectors_trs>1)
2776  {
2777  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Error while trying to parse line: " << line<< endl);
2778  Cerr(" The GATE system contains more than one 'transaxial' rsector " << endl);
2779  Cerr(" The current implementation does not support such cylindricalPET model" << endl);
2780  Cerr(" Manual implementation of the system is required (ex: model the transaxial rsectors as modules)" << endl);
2781  return 1;
2782  }
2783  }
2784 
2785 
2786  entry = "/gate/"+rsector_name+"/cubicArray/setRepeatNumberZ";
2787  values = CheckGATECommand(entry, line);
2788 
2789  if (values.size()>0)
2790  {
2791  if(ConvertFromString(values[0], &number_of_rsectors_axl) )
2792  {
2793  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2794  return 1;
2795  }
2796  }
2797 
2798 
2799  entry = "/gate/"+rsector_name+"/cubicArray/setRepeatVector";
2800  values = CheckGATECommand(entry, line);
2801  if (values.size()>0)
2802  {
2803  if(ConvertFromString(values[2], &rsector_step_axl))
2804  {
2805  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2806  return 1;
2807  }
2808  }
2809 
2810 
2811 
2812  // axial FOV computed from rsector z-length
2813  entry = "/gate/"+rsector_name+"/geometry/setZLength";
2814  values = CheckGATECommand(entry, line);
2815  if (values.size()>0)
2816  {
2817  if(ConvertFromString(values[0], &rsector_size_axl) )
2818  {
2819  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2820  return 1;
2821  }
2822 
2823  }
2824 
2825 
2826  entry = "/gate/"+rsector_name+"/ring/setFirstAngle";
2827  values = CheckGATECommand(entry, line);
2828  if (values.size()>0)
2829  {
2830  if(ConvertFromString(values[0], &rsector_first_angle) )
2831  {
2832  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2833  return 1;
2834  }
2835  }
2836 
2837  entry = "/gate/"+rsector_name+"/ring/setAngularSpan";
2838  values = CheckGATECommand(entry, line);
2839  if (values.size()>0)
2840  {
2841  if(ConvertFromString(values[0], &rsector_angular_span) )
2842  {
2843  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2844  return 1;
2845  }
2846  }
2847 
2848  // Up to 8 zshifts in Gate
2849  for (int i=1; i <8; i++)
2850  {
2851  entry = "/gate/"+rsector_name+"/ring/setZShift"+toString(i);
2852  values = CheckGATECommand(entry, line);
2853  if (values.size()>0)
2854  {
2855  double zshift = 0.;
2856  if(ConvertFromString(values[0], &zshift) )
2857  {
2858  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2859  return 1;
2860  }
2861 
2862  vec_rsector_Z_Shift.push_back(zshift);
2863  }
2864  }
2865 
2866 
2867 
2868  // --- Modules ---
2869 
2870  // Check for any translation and adjust scanner radius position accordingly
2871  entry = "/gate/"+module_name+"/placement/setTranslation";
2872  values = CheckGATECommand(entry, line);
2873 
2874  FLTNB module_pos_X =0.,
2875  module_pos_Y =0.;
2876 
2877  // Assumes that module located on the X or Y axis
2878  if (values.size()>0)
2879  {
2880  if(ConvertFromString(values[0], &module_pos_X) ||
2881  ConvertFromString(values[1], &module_pos_Y) )
2882  {
2883  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2884  return 1;
2885  }
2886 
2887  // Check first module cartesian coordinates is 0 on X or Y axis
2888  // Throw error otherwise
2889  if(module_pos_X!=0 && module_pos_Y !=0)
2890  {
2891  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2892  Cerr(" Module cartesian coordinates on either the X or Y axis expected to be equal to 0 "<< endl);
2893  return 1;
2894  }
2895 
2896  // Adjust scanner radius to the center position of module
2897  scanner_radius += is_rsector_Y_axis ? module_pos_Y : module_pos_X ;
2898  }
2899 
2900 
2901 
2902  entry = is_rsector_Y_axis ?
2903  "/gate/"+module_name+"/cubicArray/setRepeatNumberX":
2904  "/gate/"+module_name+"/cubicArray/setRepeatNumberY";
2905 
2906  values = CheckGATECommand(entry, line);
2907  if (values.size()>0)
2908  {
2909  if(ConvertFromString(values[0], &number_of_modules_trs) )
2910  {
2911  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2912  return 1;
2913  }
2914  }
2915 
2916 
2917 
2918 
2919  entry = "/gate/"+module_name+"/cubicArray/setRepeatNumberZ";
2920  values = CheckGATECommand(entry, line);
2921 
2922  if (values.size()>0)
2923  {
2924  if(ConvertFromString(values[0], &number_of_modules_axl) )
2925  {
2926  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2927  return 1;
2928  }
2929  }
2930 
2931 
2932  entry = is_rsector_Y_axis ?
2933  "/gate/"+module_name+"/geometry/setXLength":
2934  "/gate/"+module_name+"/geometry/setYLength";
2935 
2936  values = CheckGATECommand(entry, line);
2937  if (values.size()>0)
2938  {
2939  if(ConvertFromString(values[0], &module_size_trs) )
2940  {
2941  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2942  return 1;
2943  }
2944  }
2945 
2946 
2947  entry = "/gate/"+module_name+"/geometry/setZLength";
2948  values = CheckGATECommand(entry, line);
2949  if (values.size()>0)
2950  {
2951  if(ConvertFromString(values[0], &module_size_axl) )
2952  {
2953  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2954  return 1;
2955  }
2956  }
2957 
2958  entry = "/gate/"+module_name+"/cubicArray/setRepeatVector";
2959  values = CheckGATECommand(entry, line);
2960  if (values.size()>0)
2961  {
2962  string trs_step = is_rsector_Y_axis ?
2963  values[0] :
2964  values[1] ;
2965 
2966  if(ConvertFromString(trs_step, &module_step_trs) ||
2967  ConvertFromString(values[2], &module_step_axl))
2968  {
2969  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2970  return 1;
2971  }
2972  }
2973 
2974 
2975 
2976 
2977  // linear repeaters instead of box
2978  entry = "/gate/"+module_name+"/linear/setRepeatNumber";
2979  values = CheckGATECommand(entry, line);
2980 
2981  if (values.size()>0)
2982  {
2983  if(ConvertFromString(values[0] , &number_of_modules_axl) )
2984  {
2985  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2986  return 1;
2987  }
2988  }
2989 
2990  entry = "/gate/"+module_name+"/linear/setRepeatVector";
2991  values = CheckGATECommand(entry, line);
2992  if (values.size()>0)
2993  {
2994  if(ConvertFromString(values[2], &module_step_axl))
2995  {
2996  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2997  return 1;
2998  }
2999  }
3000 
3001 
3002  // --- Submodules ---
3003 
3004  // Check for any translation and adjust scanner radius position accordingly
3005  entry = "/gate/"+submodule_name+"/placement/setTranslation";
3006  values = CheckGATECommand(entry, line);
3007 
3008  FLTNB submodule_pos_X =0.,
3009  submodule_pos_Y =0.;
3010 
3011  // Assumes that submodule located on the X or Y axis
3012  if (values.size()>0)
3013  {
3014  if(ConvertFromString(values[0], &submodule_pos_X) ||
3015  ConvertFromString(values[1], &submodule_pos_Y) )
3016  {
3017  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3018  return 1;
3019  }
3020 
3021  // Check first module cartesian coordinates is 0 on X or Y axis
3022  // Throw error otherwise
3023  if(submodule_pos_X!=0 && submodule_pos_Y !=0)
3024  {
3025  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3026  Cerr(" Submodule cartesian coordinates on either the X or Y axis expected to be equal to 0 "<< endl);
3027  return 1;
3028  }
3029 
3030  // Adjust scanner radius to the center position of submodule
3031  scanner_radius += is_rsector_Y_axis ? submodule_pos_Y : submodule_pos_X ;
3032  }
3033 
3034 
3035 
3036  entry = is_rsector_Y_axis ?
3037  "/gate/"+submodule_name+"/cubicArray/setRepeatNumberX":
3038  "/gate/"+submodule_name+"/cubicArray/setRepeatNumberY";
3039 
3040 
3041  values = CheckGATECommand(entry, line);
3042  if (values.size()>0)
3043  {
3044  if(ConvertFromString(values[0], &number_of_submodules_trs) )
3045  {
3046  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3047  return 1;
3048  }
3049  }
3050 
3051  entry = "/gate/"+submodule_name+"/cubicArray/setRepeatNumberZ";
3052  values = CheckGATECommand(entry, line);
3053  if (values.size()>0)
3054  {
3055  if(ConvertFromString(values[0], &number_of_submodules_axl) )
3056  {
3057  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3058  return 1;
3059  }
3060  }
3061 
3062 
3063 
3064 
3065  entry = is_rsector_Y_axis ?
3066  "/gate/"+submodule_name+"/geometry/setXLength":
3067  "/gate/"+submodule_name+"/geometry/setYLength";
3068 
3069  values = CheckGATECommand(entry, line);
3070  if (values.size()>0)
3071  {
3072  if(ConvertFromString(values[0], &submodule_size_trs) )
3073  {
3074  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3075  return 1;
3076  }
3077  }
3078 
3079  entry = "/gate/"+submodule_name+"/geometry/setZLength";
3080  values = CheckGATECommand(entry, line);
3081  if (values.size()>0)
3082  {
3083  if(ConvertFromString(values[0], &submodule_size_axl) )
3084  {
3085  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3086  return 1;
3087  }
3088  }
3089 
3090  entry = "/gate/"+submodule_name+"/cubicArray/setRepeatVector";
3091  values = CheckGATECommand(entry, line);
3092  if (values.size()>0)
3093  {
3094  string trs_step = is_rsector_Y_axis ?
3095  values[0] :
3096  values[1] ;
3097 
3098  if(ConvertFromString(trs_step, &submodule_step_trs) ||
3099  ConvertFromString(values[2], &submodule_step_axl) )
3100  {
3101  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3102  return 1;
3103  }
3104  }
3105 
3106 
3107 
3108  // linear repeaters instead of box
3109  entry = "/gate/"+submodule_name+"/linear/setRepeatNumber";
3110  values = CheckGATECommand(entry, line);
3111 
3112  if (values.size()>0)
3113  {
3114  if(ConvertFromString( values[0] , &number_of_submodules_axl) )
3115  {
3116  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3117  return 1;
3118  }
3119  }
3120 
3121 
3122  entry = "/gate/"+submodule_name+"/linear/setRepeatVector";
3123  values = CheckGATECommand(entry, line);
3124  if (values.size()>0)
3125  {
3126  if(ConvertFromString(values[2], &submodule_step_axl))
3127  {
3128  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3129  return 1;
3130  }
3131  }
3132 
3133 
3134 
3135 
3136  // --- Crystals ---
3137 
3138  // Check for any translation and adjust scanner radius position accordingly
3139  entry = "/gate/"+crystal_name+"/placement/setTranslation";
3140  values = CheckGATECommand(entry, line);
3141 
3142  FLTNB crystal_pos_X =0.,
3143  crystal_pos_Y =0.;
3144 
3145  // Assumes that submodule located on the X or Y axis
3146  if (values.size()>0)
3147  {
3148  if(ConvertFromString(values[0], &crystal_pos_X) ||
3149  ConvertFromString(values[1], &crystal_pos_Y) )
3150  {
3151  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3152  return 1;
3153  }
3154 
3155  // Check first module cartesian coordinates is 0 on X or Y axis
3156  // Throw error otherwise
3157  if(crystal_pos_X!=0 && crystal_pos_Y !=0)
3158  {
3159  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3160  Cerr(" Crystal cartesian coordinates on either the X or Y axis expected to be equal to 0 "<< endl);
3161  return 1;
3162  }
3163 
3164  // Adjust scanner radius to the center position of crystal
3165  scanner_radius += is_rsector_Y_axis ? crystal_pos_Y : crystal_pos_X ;
3166  }
3167 
3168 
3169 
3170  entry = is_rsector_Y_axis ?
3171  "/gate/"+crystal_name+"/cubicArray/setRepeatNumberX":
3172  "/gate/"+crystal_name+"/cubicArray/setRepeatNumberY";
3173 
3174 
3175  values = CheckGATECommand(entry, line);
3176  if (values.size()>0)
3177  {
3178  if(ConvertFromString(values[0], &number_of_crystals_trs) )
3179  {
3180  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3181  return 1;
3182  }
3183  }
3184 
3185  entry = "/gate/"+crystal_name+"/cubicArray/setRepeatNumberZ";
3186  values = CheckGATECommand(entry, line);
3187  if (values.size()>0)
3188  {
3189  if(ConvertFromString(values[0], &number_of_crystals_axl) )
3190  {
3191  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3192  return 1;
3193  }
3194  }
3195 
3196 
3197  entry = "/gate/"+crystal_name+"/geometry/setXLength";
3198  values = CheckGATECommand(entry, line);
3199  if (values.size()>0)
3200  {
3201  double x_length;
3202  if(ConvertFromString(values[0], &x_length) )
3203  {
3204  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3205  return 1;
3206  }
3207 
3208  if (is_rsector_Y_axis)
3209  crystal_size_trs = x_length;
3210  else
3211  crystal_size_depth = x_length;
3212  }
3213 
3214  entry = "/gate/"+crystal_name+"/geometry/setYLength";
3215  values = CheckGATECommand(entry, line);
3216  if (values.size()>0)
3217  {
3218  double y_length;
3219  if(ConvertFromString(values[0], &y_length) )
3220  {
3221  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3222  return 1;
3223  }
3224 
3225  if (is_rsector_Y_axis)
3226  crystal_size_depth = y_length;
3227  else
3228  crystal_size_trs = y_length;
3229 
3230  }
3231 
3232  entry = "/gate/"+crystal_name+"/geometry/setZLength";
3233  values = CheckGATECommand(entry, line);
3234  if (values.size()>0)
3235  {
3236  if(ConvertFromString(values[0], &crystal_size_axl) )
3237  {
3238  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3239  return 1;
3240  }
3241  }
3242 
3243  entry = "/gate/"+crystal_name+"/cubicArray/setRepeatVector";
3244  values = CheckGATECommand(entry, line);
3245  if (values.size()>0)
3246  {
3247  string trs_step = is_rsector_Y_axis ?
3248  values[0] :
3249  values[1] ;
3250 
3251  if(ConvertFromString(trs_step, &crystal_step_trs) ||
3252  ConvertFromString(values[2], &crystal_step_axl) )
3253  {
3254  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3255  return 1;
3256  }
3257  }
3258 
3259  // linear repeaters instead of box
3260  entry = "/gate/"+crystal_name+"/linear/setRepeatNumber";
3261  values = CheckGATECommand(entry, line);
3262 
3263  if (values.size()>0)
3264  {
3265  if(ConvertFromString( values[0] , &number_of_crystals_axl) )
3266  {
3267  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3268  return 1;
3269  }
3270  }
3271 
3272  entry = "/gate/"+crystal_name+"/linear/setRepeatVector";
3273  values = CheckGATECommand(entry, line);
3274  if (values.size()>0)
3275  {
3276  if(ConvertFromString(values[2], &crystal_step_axl))
3277  {
3278  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3279  return 1;
3280  }
3281  }
3282 
3283 
3284  // --- Layers ---
3285  entry = "/gate/"+crystal_name+"/daughters/name";
3286  values = CheckGATECommand(entry, line);
3287  if (values.size()>0)
3288  {
3289  layers_names.push_back(values[0]);
3290  number_of_layers++;
3291  }
3292 
3293  // loop on the layers to get their specific information as they are integrated in layers_names.
3294  for (int i=0; i < number_of_layers; i++)
3295  {
3296  // assumes that first block is positionned on the x-axis
3297  entry = "/gate/"+layers_names[i]+"/placement/setTranslation";
3298  values = CheckGATECommand(entry, line);
3299 
3300  if (values.size()>0)
3301  {
3302  vector<double> layer_pos;
3303  for(int d=0 ; d<3 ; d++)
3304  {
3305  double pos;
3306  if(ConvertFromString(values[d], &pos) )
3307  {
3308  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3309  return 1;
3310  }
3311  layer_pos.push_back(pos);
3312  }
3313 
3314  if(is_rsector_Y_axis)
3315  {
3316  double pos = layer_pos[1];
3317  layer_pos[1] = layer_pos[0];
3318  layer_pos[0] = pos;
3319  }
3320 
3321  layers_positions.push_back(layer_pos);
3322  }
3323 
3324 
3325  // assumes that first block is positionned on the x-axis
3326  entry = "/gate/"+layers_names[i]+"/geometry/setXLength";
3327  values = CheckGATECommand(entry, line);
3328 
3329  if (values.size()>0)
3330  {
3331  double xlength;
3332  if(ConvertFromString(values[0], &xlength) )
3333  {
3334  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3335  return 1;
3336  }
3337 
3338  if (is_rsector_Y_axis)
3339  layers_size_trs.push_back(xlength);
3340  else
3341  layers_size_depth.push_back(xlength);
3342  }
3343 
3344  // assumes that first block is positionned on the x-axis
3345  entry = "/gate/"+layers_names[i]+"/geometry/setYLength";
3346  values = CheckGATECommand(entry, line);
3347  if (values.size()>0)
3348  {
3349  double ylength;
3350  if(ConvertFromString(values[0], &ylength) )
3351  {
3352  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3353  return 1;
3354  }
3355  if (is_rsector_Y_axis)
3356  layers_size_depth.push_back(ylength);
3357  else
3358  layers_size_trs.push_back(ylength);
3359  }
3360 
3361  entry = "/gate/"+layers_names[i]+"/geometry/setZLength";
3362  values = CheckGATECommand(entry, line);
3363  if (values.size()>0)
3364  {
3365  double zlength;
3366  if(ConvertFromString(values[0], &zlength) )
3367  {
3368  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3369  return 1;
3370  }
3371  layers_size_axl.push_back(zlength);
3372  }
3373 
3374  // Check if a repeater if applied to the layers elements
3375  // In this case, the total number of crystals for a layer[i]
3376  // will be crystal elts*layers elts[i]
3377  entry = is_rsector_Y_axis ?
3378  "/gate/"+layers_names[i]+"/cubicArray/setRepeatNumberX":
3379  "/gate/"+layers_names[i]+"/cubicArray/setRepeatNumberY";
3380 
3381  values = CheckGATECommand(entry, line);
3382  if (values.size()>0)
3383  {
3384  uint32_t step_trs;
3385  if(ConvertFromString(values[0], &step_trs))
3386  {
3387  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3388  return 1;
3389  }
3390  number_of_lyr_elts_trs.push_back(step_trs);
3391  }
3392 
3393  entry = "/gate/"+layers_names[i]+"/cubicArray/setRepeatNumberZ";
3394  values = CheckGATECommand(entry, line);
3395  if (values.size()>0)
3396  {
3397  uint32_t step_axl;
3398  if(ConvertFromString(values[0], &step_axl))
3399  {
3400  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3401  return 1;
3402  }
3403  number_of_lyr_elts_axl.push_back(step_axl);
3404  }
3405 
3406  entry = "/gate/"+layers_names[i]+"/cubicArray/setRepeatVector";
3407  values = CheckGATECommand(entry, line);
3408  if (values.size()>0)
3409  {
3410  string trs_step = is_rsector_Y_axis ?
3411  values[0] :
3412  values[1] ;
3413 
3414  double step_trs, step_axl;
3415  if(ConvertFromString(trs_step, &step_trs) ||
3416  ConvertFromString(values[2], &step_axl) )
3417  {
3418  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3419  return 1;
3420  }
3421 
3422 
3423  layers_step_trs.push_back(step_trs);
3424  layers_step_axl.push_back(step_axl);
3425  }
3426 
3427 
3428  // linear repeaters instead of box
3429  entry = "/gate/"+layers_names[i]+"/linear/setRepeatNumber";
3430  values = CheckGATECommand(entry, line);
3431 
3432  if (values.size()>0)
3433  {
3434  uint32_t step_axl;
3435  if(ConvertFromString( values[0] , &step_axl) )
3436  {
3437  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3438  return 1;
3439  }
3440  number_of_lyr_elts_axl.push_back(step_axl);
3441  }
3442 
3443 
3444  entry = "/gate/"+layers_names[i]+"/linear/setRepeatVector";
3445  values = CheckGATECommand(entry, line);
3446  if (values.size()>0)
3447  {
3448  double step_axl;
3449  if(ConvertFromString(values[2], &step_axl))
3450  {
3451  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3452  return 1;
3453  }
3454 
3455  layers_step_axl.push_back(step_axl);
3456  }
3457  }
3458 
3459 
3460  entry = "/gate/digitizer/Coincidences/minSectorDifference";
3461  values = CheckGATECommand(entry, line);
3462  if (values.size()>0)
3463  {
3464  FLTNB min_sector_diff= 0.;
3465 
3466  if(ConvertFromString(values[0], &min_sector_diff))
3467  {
3468  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3469  return 1;
3470  }
3471 
3472  min_angle_diff = 360./number_of_rsectors_ang*min_sector_diff;
3473  }
3474 
3475  }
3476  systemMac.close();
3477 
3478  }
3479 
3480  // Compute total number of crystal elements
3481  if(number_of_layers == 0)
3482  {
3483  number_of_elements = number_of_rsectors_ang
3484  * number_of_rsectors_axl
3485  * number_of_modules_trs
3486  * number_of_modules_axl
3487  * number_of_submodules_trs
3488  * number_of_submodules_axl
3489  * number_of_crystals_trs
3490  * number_of_crystals_axl;
3491  }
3492  else
3493  for(int l=0 ; l<number_of_layers ; l++)
3494  {
3495  int32_t nb_crystals_layer = number_of_rsectors_ang
3496  * number_of_rsectors_axl
3497  * number_of_modules_trs
3498  * number_of_modules_axl
3499  * number_of_submodules_trs
3500  * number_of_submodules_axl
3501  * number_of_crystals_trs
3502  * number_of_crystals_axl;
3503 
3504  // Add layer elements if repeaters have been used on layers
3505  if(number_of_lyr_elts_trs.size()>0 || number_of_lyr_elts_axl.size()>0 )
3506  nb_crystals_layer *= number_of_lyr_elts_trs[l] * number_of_lyr_elts_axl[l];
3507 
3508  number_of_elements += nb_crystals_layer;
3509  }
3510 
3511 
3512  // Compute sizes and gaps of each cylindrical PET structures.
3513 
3514  // Compute crystal gaps
3515  if (crystal_step_axl - crystal_size_axl >= 0)
3516  crystal_gap_axl = crystal_step_axl - crystal_size_axl;
3517 
3518  if (crystal_step_trs - crystal_size_trs >= 0)
3519  crystal_gap_trs = crystal_step_trs - crystal_size_trs;
3520 
3521  // Compute submodule size from crystals size, gaps & number
3522  submodule_size_axl = crystal_size_axl*number_of_crystals_axl + crystal_gap_axl*(number_of_crystals_axl-1);
3523  submodule_size_trs = crystal_size_trs*number_of_crystals_trs + crystal_gap_trs*(number_of_crystals_trs-1);
3524 
3525  // Compute submodule gaps
3526  if (submodule_step_axl - submodule_size_axl >= 0)
3527  submodule_gap_axl = submodule_step_axl - submodule_size_axl;
3528 
3529  if (submodule_step_trs - submodule_size_trs >= 0)
3530  submodule_gap_trs = submodule_step_trs - submodule_size_trs;
3531 
3532  // Compute module size from submodule size, gaps & number
3533  module_size_axl = submodule_size_axl*number_of_submodules_axl + submodule_gap_axl*(number_of_submodules_axl-1);
3534  module_size_trs = submodule_size_trs*number_of_submodules_trs + submodule_gap_trs*(number_of_submodules_trs-1);
3535 
3536  // Compute module gaps
3537  if (module_step_axl - module_size_axl >= 0)
3538  module_gap_axl = module_step_axl - module_size_axl;
3539 
3540  if (module_step_trs - module_size_trs >= 0)
3541  module_gap_trs = module_step_trs - module_size_trs;
3542 
3543  // Compute rsector size from crystals size, gaps & number
3544  rsector_size_axl = module_size_axl*number_of_modules_axl + module_gap_axl*(number_of_modules_axl-1);
3545  rsector_size_trs = module_size_trs*number_of_modules_trs + module_gap_trs*(number_of_modules_trs-1);
3546 
3547 
3548  // Compute rsector gaps.
3549  if (rsector_step_axl - rsector_size_axl >= 0)
3550  rsector_gap_axl = rsector_step_axl - rsector_size_axl;
3551 
3552 
3553  // Axial FOV
3554  fov_axl = rsector_size_axl * number_of_rsectors_axl // rsectors
3555  + (number_of_rsectors_axl-1)*rsector_gap_axl; // gaps
3556 
3557  // Just use twice the total number of crystals to arbitrary define a number of voxels
3558  //voxels_number_axl = fov_axl/4 + 1;
3559  voxels_number_axl = ( number_of_crystals_axl
3560  * number_of_submodules_axl
3561  * number_of_modules_axl
3562  * number_of_rsectors_axl )
3563  * 2;
3564 
3565  // Transaxial FOV defined as half diameter/1.5
3566  fov_trs = (2*scanner_radius ) / 1.5;
3567 
3568  // Arbitrary define the transaxial voxel size as axial_voxel_size / 1.5
3569  //voxels_number_trs = fov_trs/4 + 1;
3570  voxels_number_trs = ( number_of_crystals_trs
3571  * number_of_submodules_trs
3572  * number_of_modules_trs
3573  * number_of_rsectors_ang )
3574  / 3;
3575 
3576 
3577  // Compute first angle
3578  // CASToR x and y axis for rotation inverted in comparison with GATE
3579  rsector_first_angle -= round(atan2f(rsector_pos_X , rsector_pos_Y) * 180. / M_PI);
3580 
3581  // Computing the other parts of variables, if their is more than one layer
3582  if (number_of_layers > 0)
3583  {
3584  for (int l=0; l < number_of_layers ; l++)
3585  {
3586  // Scanner vectors
3587  vec_scanner_radius.push_back( toString(scanner_radius
3588  +layers_positions[ l ][ 0 ]
3589  -layers_size_depth[ l ] / 2. ) );
3590 
3591  // Rsector vectors
3592  vec_number_of_rsectors_ang.push_back( toString(number_of_rsectors_ang) );
3593  vec_number_of_rsectors_axl.push_back( toString(number_of_rsectors_axl) );
3594  vec_rsector_gap_axl.push_back( toString(rsector_gap_axl) );
3595  vec_rsector_first_angle.push_back( toString(rsector_first_angle) );
3596 
3597  // Module vectors
3598  vec_number_of_modules_trs.push_back( toString(number_of_modules_trs) );
3599  vec_number_of_modules_axl.push_back( toString(number_of_modules_axl) );
3600  vec_module_gap_trs.push_back( toString(module_gap_trs) );
3601  vec_module_gap_axl.push_back( toString(module_gap_axl) );
3602 
3603  // Submodule vectors
3604  vec_number_of_submodules_trs.push_back( toString(number_of_submodules_trs) );
3605  vec_number_of_submodules_axl.push_back( toString(number_of_submodules_axl) );
3606  vec_submodule_gap_trs.push_back( toString(submodule_gap_trs) );
3607  vec_submodule_gap_axl.push_back( toString(submodule_gap_axl) );
3608 
3609  // Crystal vectors
3610  uint32_t nb_tot_trs_cry = (number_of_lyr_elts_trs.size()>0) ?
3611  number_of_lyr_elts_trs[l]*number_of_crystals_trs :
3612  number_of_crystals_trs ;
3613 
3614  uint32_t nb_tot_axl_cry = (number_of_lyr_elts_axl.size()>0) ?
3615  number_of_lyr_elts_axl[l]*number_of_crystals_axl :
3616  number_of_crystals_axl ;
3617 
3618  vec_number_of_crystals_trs.push_back( toString(nb_tot_trs_cry) );
3619  vec_number_of_crystals_axl.push_back( toString(nb_tot_axl_cry) );
3620 
3621 
3622  // Compute the gap from the layer variables
3623  // If those variables have not been set, use the crystals gap previously computed
3624  if(layers_step_trs.size()>0 ||
3625  layers_step_axl.size()>0)
3626  {
3627  if (layers_step_trs[l] - layers_size_trs[l] >= 0)
3628  crystal_gap_trs = layers_step_trs[l] - layers_size_trs[l];
3629 
3630  if (layers_step_axl[l] - layers_size_axl[l] >= 0)
3631  crystal_gap_axl = layers_step_axl[l] - layers_size_axl[l];
3632  }
3633 
3634  vec_crystal_gap_trs.push_back( toString(crystal_gap_trs) );
3635  vec_crystal_gap_axl.push_back( toString(crystal_gap_axl) );
3636 
3637  // Optionnal
3638  vec_mean_depth_of_interaction.push_back( toString(mean_depth_of_interaction) );
3639  vec_min_angle_diff.push_back( toString(min_angle_diff) );
3640  }
3641  }
3642  else
3643  {
3644  // Layer dimensions = crystal dimensions
3645  layers_size_depth.push_back( crystal_size_depth );
3646  layers_size_trs.push_back( crystal_size_trs );
3647  layers_size_axl.push_back( crystal_size_axl );
3648 
3649 
3650  // Scanner vectors
3651  vec_scanner_radius.push_back( toString(scanner_radius - crystal_size_depth/2) );
3652 
3653  // Rsector vectors
3654  vec_number_of_rsectors_ang.push_back( toString(number_of_rsectors_ang) );
3655  vec_number_of_rsectors_axl.push_back( toString(number_of_rsectors_axl) );
3656  vec_rsector_gap_axl.push_back( toString(rsector_gap_axl) );
3657  vec_rsector_first_angle.push_back( toString(rsector_first_angle) );
3658 
3659  // Module vectors
3660  vec_number_of_modules_trs.push_back( toString(number_of_modules_trs) );
3661  vec_number_of_modules_axl.push_back( toString(number_of_modules_axl) );
3662  vec_module_gap_trs.push_back( toString(module_gap_trs) );
3663  vec_module_gap_axl.push_back( toString(module_gap_axl) );
3664 
3665  // Submodule vectors
3666  vec_number_of_submodules_trs.push_back( toString(number_of_submodules_trs) );
3667  vec_number_of_submodules_axl.push_back( toString(number_of_submodules_axl) );
3668  vec_submodule_gap_trs.push_back( toString(submodule_gap_trs) );
3669  vec_submodule_gap_axl.push_back( toString(submodule_gap_axl) );
3670 
3671  // Crystal vectors
3672  vec_number_of_crystals_trs.push_back( toString(number_of_crystals_trs) );
3673  vec_number_of_crystals_axl.push_back( toString(number_of_crystals_axl) );
3674  vec_crystal_gap_trs.push_back( toString(crystal_gap_trs) );
3675  vec_crystal_gap_axl.push_back( toString(crystal_gap_axl) );
3676 
3677  // Optionnal
3678  vec_mean_depth_of_interaction.push_back( toString(mean_depth_of_interaction) );
3679  vec_min_angle_diff.push_back( toString(min_angle_diff) );
3680 
3681  // Update the real number of layers
3682  number_of_layers = 1;
3683  }
3684 
3685 
3686  // Write the .geom file
3687  ofstream fileGeom(a_pathGeom.c_str(), ios::out | ios::trunc);
3688  if(fileGeom)
3689  {
3690  fileGeom <<"# comments" << endl;
3691  fileGeom <<"# Y _________ "<< endl;
3692  fileGeom <<"# | / _ \\ \\ "<< endl;
3693  fileGeom <<"# | | / \\ | |"<< endl;
3694  fileGeom <<"# |_____ Z | | | | |"<< endl;
3695  fileGeom <<"# \\ | | | | |" << endl;
3696  fileGeom <<"# \\ | \\_/ | |" << endl;
3697  fileGeom <<"# X \\___/_____/" << endl;
3698  fileGeom <<"# Left-handed axis orientation"<< endl;
3699  fileGeom <<"# scanner axis is z" << endl;
3700  fileGeom <<"# positions in millimeters"<< endl;
3701  fileGeom <<"# Use comma without space as separator in the tables." << endl;
3702 
3703  fileGeom << ""<< endl;
3704 
3705  // Mandatory fields
3706  fileGeom << "# MANDATORY FIELDS"<< endl;
3707  fileGeom << "modality : " << modality << endl;
3708  fileGeom << "scanner name : " << scanner_name << endl;
3709  fileGeom << "number of elements : " << number_of_elements << endl;
3710  fileGeom << "number of layers : " << number_of_layers << endl;
3711  fileGeom << "" << endl;
3712  fileGeom << "voxels number transaxial : " << voxels_number_trs << endl;
3713  fileGeom << "voxels number axial : " << voxels_number_axl << endl;
3714 
3715  fileGeom << "field of view transaxial : " << fov_trs << endl;
3716  fileGeom << "field of view axial : " << fov_axl << endl << endl;
3717  fileGeom << "description : " << description << endl;
3718  fileGeom << "" << endl;
3719  WriteVector(fileGeom,"scanner radius : ",vec_scanner_radius);
3720  WriteVector(fileGeom,"number of rsectors : ",vec_number_of_rsectors_ang);
3721  WriteVector(fileGeom,"number of crystals transaxial : ",vec_number_of_crystals_trs);
3722  WriteVector(fileGeom, "number of crystals axial : ",vec_number_of_crystals_axl);
3723  fileGeom << ""<< endl;
3724  WriteVector(fileGeom, "crystals size depth : ", layers_size_depth);
3725  WriteVector(fileGeom, "crystals size transaxial : ", layers_size_trs);
3726  WriteVector(fileGeom, "crystals size axial : ", layers_size_axl);
3727  fileGeom << ""<< endl;
3728  fileGeom << ""<< endl;
3729 
3730  // Optional fields
3731  fileGeom << "# OPTIONAL FIELDS"<< endl;
3732  WriteVector(fileGeom,"rsectors first angle : ",vec_rsector_first_angle);
3733  WriteVector(fileGeom, "number of rsectors axial : ",vec_number_of_rsectors_axl);
3734  WriteVector(fileGeom,"rsector gap transaxial : ",vec_rsector_gap_trs);
3735  WriteVector(fileGeom,"rsector gap axial : ",vec_rsector_gap_axl);
3736  WriteVector(fileGeom,"number of modules transaxial : ",vec_number_of_modules_trs);
3737  WriteVector(fileGeom, "number of modules axial : ",vec_number_of_modules_axl);
3738  WriteVector(fileGeom,"module gap transaxial : ",vec_module_gap_trs);
3739  WriteVector(fileGeom,"module gap axial : ",vec_module_gap_axl);
3740  WriteVector(fileGeom,"number of submodules transaxial : ",vec_number_of_submodules_trs);
3741  WriteVector(fileGeom, "number of submodules axial : ",vec_number_of_submodules_axl);
3742  WriteVector(fileGeom,"submodule gap transaxial : ",vec_submodule_gap_trs);
3743  WriteVector(fileGeom,"submodule gap axial : ",vec_submodule_gap_axl);
3744  WriteVector(fileGeom,"crystal gap transaxial : ",vec_crystal_gap_trs);
3745  WriteVector(fileGeom,"crystal gap axial : ",vec_crystal_gap_axl);
3746  WriteVector(fileGeom, "mean depth of interaction : ", vec_mean_depth_of_interaction);
3747  fileGeom << "rotation direction : CCW " << endl; // default for GATE
3748  fileGeom << ""<< endl;
3749 
3750  // Write only if different from default values
3751  if(min_angle_diff > 0.) fileGeom << "min angle difference : " << min_angle_diff << endl;
3752 
3753  // Convert angular span to the CASToR convention
3754  if(rsector_angular_span >= 360.0005 ||
3755  rsector_angular_span <= 359.9995 ) // GATE bounds
3756  {
3757  rsector_angular_span *= (double)number_of_rsectors_ang/(double)(number_of_rsectors_ang-1);
3758  fileGeom << "rsectors angular span : " << rsector_angular_span << endl;
3759  }
3760 
3761  if(rsector_nb_zshifts > 0) fileGeom << "rsectors nbZShift :" << rsector_nb_zshifts << endl;
3762  if(!vec_rsector_Z_Shift.empty()) WriteVector(fileGeom, "rsectors ZShift : ", vec_rsector_Z_Shift);
3763 
3764  fileGeom.close();
3765 
3766  cout << "Output geom file written at :" << a_pathGeom << endl;
3767  }
3768  else
3769  {
3770  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Couldn't open geom file for writing "<< a_pathGeom << " !" << endl);
3771  return 1;
3772  }
3773 
3774 
3775 
3776  return 0;
3777 }
3778 
3779 
3780 
3781 // =====================================================================
3782 // ---------------------------------------------------------------------
3783 // ---------------------------------------------------------------------
3784 // =====================================================================
3785 /*
3786  \fn CreateGeomWithECAT()
3787  \param a_pathMac : string containing the path to a GATE macro file
3788  \param a_pathGeom : string containing the path to a CASToR output geom file
3789  \brief Read a GATE macro file containing the description of an ecat system, and convert it to a geom file
3790  \return 0 if success, positive value otherwise
3791 */
3792 int CreateGeomWithECAT(string a_pathMac, string a_pathGeom)
3793 {
3794  /* Declaring variables */
3795 
3796  // Scanner
3797  string modality;
3798  string scanner_name = GetFileFromPath(a_pathGeom.substr(0,a_pathGeom.find_first_of(".geom")));
3799  double scanner_radius = 0.;
3800  uint32_t number_of_elements = 0;
3801  string description = "ECAT system extracted from GATE macro: " + a_pathMac;
3802 
3803  // blocks
3804  string block_name = "block";
3805  uint32_t number_of_blocks = 1;
3806  uint32_t number_of_blocks_trs = 1;
3807  uint32_t number_of_blocks_axl = 1;
3808  double block_step_trs = 0.;
3809  double block_step_axl = 0.;
3810  double block_gap_trs = 0.;
3811  double block_gap_axl = 0.;
3812  double block_size_Y;
3813  double block_size_Z;
3814  double block_pos_X = 0.;
3815  double block_pos_Y = 0.;
3816  double block_first_angle = 0.;
3817  double block_angular_span = 360.;
3818  uint32_t block_nb_zshifts = 0;
3819  vector <double> vec_block_Z_Shift;
3820  bool is_block_Y_axis = false;
3821 
3822 
3823 
3824  // Crystals
3825  string crystal_name = "crystal";
3826  uint32_t number_of_crystals_trs = 1;
3827  uint32_t number_of_crystals_axl = 1;
3828  double crystal_step_trs = 0.;
3829  double crystal_step_axl = 0.;
3830  double crystal_gap_trs = 0.;
3831  double crystal_gap_axl = 0.;
3832  double crystal_size_depth = 0.;
3833  double crystal_size_trs = 0.;
3834  double crystal_size_axl = 0.;
3835 
3836  // Optional
3837  uint32_t voxels_number_trs;
3838  uint32_t voxels_number_axl;
3839  double fov_trs;
3840  double fov_axl;
3841  double min_angle_diff = 0.;
3842 
3843  vector<string> path_mac_files;
3844  path_mac_files.push_back(a_pathMac);
3845 
3846  // Recover path to all macro files from the main mac file
3847  if(GetGATEMacFiles(a_pathMac , path_mac_files))
3848  {
3849  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()->Error occurred when trying to recover paths to GATE macro files !" << endl);
3850  return 1;
3851  }
3852 
3853 
3854  // Recover aliases of the different parts of the architecture
3855  if(GetGATEAliasesEcat(path_mac_files, block_name, crystal_name, 2) )
3856  {
3857  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()->Error occurred when trying to recover aliases for the elements of the ecat !" << endl);
3858  return 1;
3859  }
3860 
3861 
3862  // Loop to recover all other informations
3863  for(uint16_t f=0 ; f<path_mac_files.size() ; f++)
3864  {
3865  ifstream systemMac(path_mac_files[f].c_str(), ios::in);
3866 
3867  string line;
3868  while(getline(systemMac, line))
3869  {
3870  // Scanner
3871  modality = "PET";
3872  vector <string> values;
3873  string entry = "";
3874 
3875 
3876  entry = "/gate/"+block_name+"/placement/setTranslation";
3877  values = CheckGATECommand(entry, line);
3878  if (values.size()>0)
3879  {
3880  if(ConvertFromString(values[0], &block_pos_X) ||
3881  ConvertFromString(values[1], &block_pos_Y) )
3882  {
3883  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
3884  return 1;
3885  }
3886 
3887  // Check first block cartesian coordinates is 0 on X or Y axis
3888  // Throw error otherwise
3889  if(block_pos_X!=0 && block_pos_Y !=0)
3890  {
3891  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
3892  Cerr(" Block cartesian coordinates on either the X or Y axis expected to be equal to 0 "<< endl);
3893  return 1;
3894  }
3895 
3896  // Get the axis on which the first rsector is positionned
3897  if(block_pos_Y!=0) is_block_Y_axis = true;
3898 
3899  scanner_radius += is_block_Y_axis ? abs(block_pos_Y) : abs(block_pos_X) ;
3900  }
3901 
3902 
3903  // Old version accounting on the rsector depth to be equal to the detector depth
3904  // in order to get the position of the detector surface.
3905  // Current version tracks the 'setTranslation' commands to get to the actual detector surface position
3906  /*
3907  entry = is_block_Y_axis ?
3908  "/gate/"+block_name+"/geometry/setYLength" :
3909  "/gate/"+block_name+"/geometry/setXLength";
3910 
3911  values = CheckGATECommand(entry, line);
3912  if (values.size()>0)
3913  {
3914  double block_size = 0.;
3915  if(ConvertFromString(values[0], &block_size) )
3916  {
3917  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3918  return 1;
3919  }
3920 
3921  scanner_radius -= block_size/2;
3922  }
3923  */
3924 
3925 
3926  entry = "/gate/"+block_name+"/geometry/setZLength";
3927  values = CheckGATECommand(entry, line);
3928  if (values.size()>0)
3929  {
3930  if(ConvertFromString(values[0], &fov_axl))
3931  {
3932  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
3933  return 1;
3934  }
3935  // 4mm voxels by default
3936  voxels_number_axl = fov_axl/4 + 1;
3937  }
3938 
3939 
3940  // blocks
3941  entry = "/gate/"+block_name+"/ring/setRepeatNumber";
3942  values = CheckGATECommand(entry, line);
3943  if (values.size()>0)
3944  {
3945  if(ConvertFromString(values[0], &number_of_blocks))
3946  {
3947  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
3948  return 1;
3949  }
3950  }
3951 
3952 
3953  entry = "/gate/"+block_name+"/ring/setFirstAngle";
3954  values = CheckGATECommand(entry, line);
3955  if (values.size()>0)
3956  {
3957  if(ConvertFromString(values[0], &block_first_angle))
3958  {
3959  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
3960  return 1;
3961  }
3962  }
3963 
3964 
3965  entry = "/gate/"+block_name+"/ring/setAngularSpan";
3966  values = CheckGATECommand(entry, line);
3967  if (values.size()>0)
3968  {
3969  if(ConvertFromString(values[0], &block_angular_span))
3970  {
3971  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
3972  return 1;
3973  }
3974  }
3975 
3976  entry = "/gate/"+block_name+"/ring/setModuloNumber";
3977  values = CheckGATECommand(entry, line);
3978  if (values.size()>0)
3979  {
3980  if(ConvertFromString(values[0], &block_nb_zshifts) )
3981  {
3982  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3983  return 1;
3984  }
3985  }
3986 
3987  for (int i=1; i<8; i++)
3988  {
3989  entry = "/gate/"+block_name+"/ring/setZShift"+toString(i);
3990  values = CheckGATECommand(entry, line);
3991  if (values.size()>0)
3992  {
3993  double zshift;
3994  if(ConvertFromString(values[0], &zshift))
3995  {
3996  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
3997  return 1;
3998  }
3999  vec_block_Z_Shift.push_back(zshift);
4000  }
4001  }
4002 
4003 
4004 
4005 
4006  // Modules
4007  entry = "/gate/"+block_name+"/linear/setRepeatNumber";
4008  values = CheckGATECommand(entry, line);
4009  if (values.size()>0)
4010  {
4011  if(ConvertFromString(values[0], &number_of_blocks_axl))
4012  {
4013  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4014  return 1;
4015  }
4016  }
4017 
4018 
4019  entry = "/gate/"+block_name+"/linear/setRepeatVector";
4020  values = CheckGATECommand(entry, line);
4021  if (values.size()>0)
4022  {
4023  if(ConvertFromString(values[2], &block_step_axl))
4024  {
4025  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4026  return 1;
4027  }
4028  }
4029 
4030 
4031 
4032  // Crystals
4033 
4034  entry = "/gate/"+crystal_name+"/placement/setTranslation";
4035  values = CheckGATECommand(entry, line);
4036 
4037  FLTNB crystal_pos_X =0.,
4038  crystal_pos_Y =0.;
4039 
4040  if (values.size()>0)
4041  {
4042  if(ConvertFromString(values[0], &crystal_pos_X) ||
4043  ConvertFromString(values[1], &crystal_pos_Y) )
4044  {
4045  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4046  return 1;
4047  }
4048 
4049  // Check first crystal cartesian coordinates is 0 on X or Y axis
4050  // Throw error otherwise
4051  if(crystal_pos_X!=0 && crystal_pos_Y !=0)
4052  {
4053  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4054  Cerr(" Block cartesian coordinates on either the X or Y axis expected to be equal to 0 "<< endl);
4055  return 1;
4056  }
4057 
4058  scanner_radius += is_block_Y_axis ? crystal_pos_Y : crystal_pos_X ;
4059  }
4060 
4061  // Adjust scanner radius (from center to block surface) according to crystal size
4062  entry = is_block_Y_axis ?
4063  "/gate/"+crystal_name+"/geometry/setYLength" :
4064  "/gate/"+crystal_name+"/geometry/setXLength";
4065 
4066  values = CheckGATECommand(entry, line);
4067  if (values.size()>0)
4068  {
4069  double crystal_size = 0.;
4070  if(ConvertFromString(values[0], &crystal_size) )
4071  {
4072  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4073  return 1;
4074  }
4075 
4076  scanner_radius -= crystal_size/2;
4077  }
4078 
4079 
4080 
4081  // assumes that first block is positionned on the x-axis
4082  entry = is_block_Y_axis ?
4083  "/gate/"+crystal_name+"/cubicArray/setRepeatNumberX":
4084  "/gate/"+crystal_name+"/cubicArray/setRepeatNumberY";
4085 
4086  values = CheckGATECommand(entry, line);
4087  if (values.size()>0)
4088  {
4089  if(ConvertFromString(values[0], &number_of_crystals_trs))
4090  {
4091  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4092  return 1;
4093  }
4094  }
4095 
4096  entry = "/gate/"+crystal_name+"/cubicArray/setRepeatNumberZ";
4097  values = CheckGATECommand(entry, line);
4098  if (values.size()>0)
4099  {
4100  if(ConvertFromString(values[0], &number_of_crystals_axl))
4101  {
4102  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4103  return 1;
4104  }
4105  }
4106 
4107 
4108  entry = "/gate/"+crystal_name+"/cubicArray/setRepeatVector";
4109  values = CheckGATECommand(entry, line);
4110  if (values.size()>0)
4111  {
4112  string trs_step = is_block_Y_axis ?
4113  values[0] :
4114  values[1] ;
4115 
4116  if(ConvertFromString(values[1], &crystal_step_trs) ||
4117  ConvertFromString(values[2], &crystal_step_axl) )
4118  {
4119  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4120  return 1;
4121  }
4122  }
4123 
4124 
4125  // assumes that first block is positionned on the x-axis
4126  entry = "/gate/"+crystal_name+"/geometry/setXLength";
4127  values = CheckGATECommand(entry, line);
4128  if (values.size()>0)
4129  {
4130  double x_length;
4131  if(ConvertFromString(values[0], &x_length))
4132  {
4133  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4134  return 1;
4135  }
4136 
4137  if (is_block_Y_axis)
4138  crystal_size_trs = x_length;
4139  else
4140  crystal_size_depth = x_length;
4141  }
4142 
4143 
4144  // assumes that first block is positionned on the x-axis
4145  entry = "/gate/"+crystal_name+"/geometry/setYLength";
4146  values = CheckGATECommand(entry, line);
4147  if (values.size()>0)
4148  {
4149  double y_length;
4150  if(ConvertFromString(values[0], &y_length))
4151  {
4152  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4153  return 1;
4154  }
4155 
4156  if (is_block_Y_axis)
4157  crystal_size_depth = y_length;
4158  else
4159  crystal_size_trs = y_length;
4160  }
4161 
4162  entry = "/gate/"+crystal_name+"/geometry/setZLength";
4163  values = CheckGATECommand(entry, line);
4164  if (values.size()>0)
4165  {
4166  if(ConvertFromString(values[0], &crystal_size_axl))
4167  {
4168  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4169  return 1;
4170  }
4171  }
4172 
4173 
4174 
4175 
4176  entry = "/gate/digitizer/Coincidences/minSectorDifference";
4177  values = CheckGATECommand(entry, line);
4178  if (values.size()>0)
4179  {
4180  double min_sector_diff;
4181  if(ConvertFromString(values[0], &min_sector_diff))
4182  {
4183  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4184  return 1;
4185  }
4186  min_angle_diff = 360/number_of_blocks*min_sector_diff;
4187  }
4188 
4189  }
4190  systemMac.close();
4191  }
4192 
4193  number_of_elements = number_of_blocks *
4194  number_of_blocks_axl *
4195  number_of_crystals_trs *
4196  number_of_crystals_axl;
4197 
4198 
4199 
4200  // Transaxial FOV defined as scanner radius / 2
4201  fov_trs = (2*scanner_radius ) / 1.5;
4202 
4203  // 4mm voxels by default
4204  voxels_number_trs = ( number_of_crystals_trs
4205  * number_of_blocks_trs)
4206  / 3;
4207 
4208 
4209  if (crystal_step_axl - crystal_size_axl >= 0)
4210  crystal_gap_axl = crystal_step_axl - crystal_size_axl;
4211 
4212  if (crystal_step_trs - crystal_size_trs >= 0)
4213  crystal_gap_trs = crystal_step_trs - crystal_size_trs;
4214 
4215 
4216  // Compute submodule size from crystals size, gaps & number
4217  block_size_Z = crystal_size_axl*number_of_crystals_axl + crystal_gap_axl*(number_of_crystals_axl-1);
4218  block_size_Y = crystal_size_trs*number_of_crystals_trs + crystal_gap_trs*(number_of_crystals_trs-1);
4219 
4220  // Compute submodule gaps
4221  if (block_step_axl - block_size_Z >= 0)
4222  block_gap_axl = block_step_axl - block_size_Z;
4223 
4224  if (block_step_trs - block_size_Y >= 0)
4225  block_gap_trs = block_step_trs - block_size_Y;
4226 
4227  // CASToR x and y axis for rotation inverted in comparison with GATE
4228  block_first_angle -= round(atan2f(block_pos_X , block_pos_Y) * 180. / M_PI);
4229 
4230 
4231  // Writing the .geom file
4232  ofstream fileGeom(a_pathGeom.c_str(), ios::out | ios::trunc);
4233 
4234  if(fileGeom)
4235  {
4236  fileGeom <<"# comments" << endl;
4237  fileGeom <<"# Y _________ "<< endl;
4238  fileGeom <<"# | / _ \\ \\ "<< endl;
4239  fileGeom <<"# | | / \\ | |"<< endl;
4240  fileGeom <<"# |_____ Z | | | | |"<< endl;
4241  fileGeom <<"# \\ | | | | |" << endl;
4242  fileGeom <<"# \\ | \\_/ | |" << endl;
4243  fileGeom <<"# X \\___/_____/" << endl;
4244  fileGeom <<"# Left-handed axis orientation"<< endl;
4245  fileGeom <<"# scanner axis is z" << endl;
4246  fileGeom <<"# positions in millimeters"<< endl;
4247  fileGeom <<"# Use comma without space as separator in the tables." << endl;
4248 
4249 
4250  fileGeom << "modality : " << modality << endl;
4251  fileGeom << "scanner name : " << scanner_name << endl;
4252  fileGeom << "number of elements : " << number_of_elements << endl;
4253  fileGeom << "number of layers : " << "1" << endl;
4254  fileGeom << "" << endl;
4255  fileGeom << "# default reconstruction parameters" << endl;
4256  fileGeom << "voxels number transaxial : " << voxels_number_trs << endl;
4257  fileGeom << "voxels number axial : " << voxels_number_axl << endl;
4258 
4259  fileGeom << "field of view transaxial : " << fov_trs << endl;
4260  fileGeom << "field of view axial : " << fov_axl << endl;
4261  fileGeom << "" << endl;
4262  fileGeom << "description : " << description << endl;
4263  fileGeom << "" << endl;
4264  fileGeom << "scanner radius : " << scanner_radius << endl;
4265  fileGeom << "number of rsectors : " << number_of_blocks << endl;
4266  fileGeom << "number of crystals transaxial : " << number_of_crystals_trs << endl;
4267  fileGeom << "number of crystals axial : " << number_of_crystals_axl << endl;
4268 
4269  fileGeom << ""<< endl;
4270  fileGeom << "# The 4 following parameters could be defined in arrays (SizeLayer1,SizeLayer2,SizeLayer3,etc..) if their is more than one layer"<< endl;
4271  fileGeom << "crystals size depth : " << crystal_size_depth << endl;
4272  fileGeom << "crystals size transaxial : " << crystal_size_trs << endl;
4273  fileGeom << "crystals size axial : " << crystal_size_axl << endl;
4274  fileGeom << ""<< endl;
4275 
4276  // Optional fields
4277  fileGeom << "rsectors first angle : " << block_first_angle << endl;
4278  fileGeom << "number of modules transaxial : " << number_of_blocks_trs << endl;
4279  fileGeom << "number of modules axial : " << number_of_blocks_axl << endl;
4280  fileGeom << "module gap transaxial : " << block_gap_trs << endl;
4281  fileGeom << "module gap axial : " << block_gap_axl << endl;
4282  fileGeom << "crystal gap transaxial : " << crystal_gap_trs << endl;
4283  fileGeom << "crystal gap axial : " << crystal_gap_axl << endl;
4284  fileGeom << "rotation direction : CCW " << endl; // default for GATE
4285  fileGeom << ""<< endl;
4286 
4287  // Write only if different from default values
4288  if(min_angle_diff > 0.) fileGeom << "min angle difference : " << min_angle_diff << endl;
4289 
4290  // Convert angular span to the CASToR convention
4291  if(block_angular_span >= 360.0005 ||
4292  block_angular_span <= 359.9995 ) // GATE bounds
4293  {
4294  block_angular_span *= (double)(number_of_blocks)/(double)(number_of_blocks-1);
4295  fileGeom << "rsectors angular span : " << block_angular_span << endl;
4296  }
4297 
4298  if(block_nb_zshifts > 0) fileGeom << "rsectors nbZShift :" << block_nb_zshifts << endl;
4299  if(!vec_block_Z_Shift.empty()) WriteVector(fileGeom, "rsectors ZShift : ", vec_block_Z_Shift);
4300 
4301  fileGeom.close();
4302 
4303  Cout("Output geom file written at :" << a_pathGeom << endl);
4304  }
4305  else
4306  {
4307  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Couldn't open geom file for writing "<< a_pathGeom << " !" << endl);
4308  return 1;
4309  }
4310 
4311  return 0;
4312 }
4313 
4314 
4315 
4316 
4317 // =====================================================================
4318 // ---------------------------------------------------------------------
4319 // ---------------------------------------------------------------------
4320 // =====================================================================
4321 /*
4322  \fn CreateGeomWithSPECT()
4323  \param a_pathMac : string containing the path to a GATE macro file
4324  \param a_pathGeom : string containing the path to a CASToR output geom file
4325  \brief Read a GATE macro file containing the description of a SPECThead system, and convert it to a geom file
4326  \return 0 if success, positive value otherwise
4327 */
4328 int CreateGeomWithSPECT(string a_pathMac, string a_pathGeom)
4329 {
4330  /* Declaring variables */
4331  string modality;
4332  string scanner_name = GetFileFromPath(a_pathGeom.substr(0,a_pathGeom.find_first_of(".geom")));
4333  FLTNB scanner_radius = -1.;
4334  string description = "SPECT camera extracted from GATE macro: " + a_pathMac;
4335 
4336  // head(s)
4337  string head_name = "SPECThead"; // not used. Correspond to SPECThead
4338  uint32_t number_of_heads = 0;
4339  FLTNB head_pos_X = 0.;
4340  FLTNB head_pos_Y = 0.;
4341  FLTNB head_first_angle = 0.;
4342  FLTNB head_angular_pitch = -1.;
4343  string head_orbit_name = "";
4344  FLTNB head_rotation_speed = 0.;
4345  bool is_head_Y_axis = false;
4346 
4347  // crystal
4348  string crystal_name = "crystal";
4349  FLTNB crystal_size_trs = 0.;
4350  FLTNB crystal_size_axl = 0.;
4351  FLTNB crystal_depth = 0;
4352 
4353  // pixel
4354  string pixel_name = "pixel";
4355  uint32_t number_of_pixels_trs = 1;
4356  uint32_t number_of_pixels_axl = 1;
4357  FLTNB pix_size_trs = 0.;
4358  FLTNB pix_size_axl = 0.;
4359  FLTNB pix_step_trs = 0.;
4360  FLTNB pix_step_axl = 0.;
4361  FLTNB pix_gap_trs = 0.;
4362  FLTNB pix_gap_axl = 0.;
4363 
4364  // collimator parameters
4365  string focal_model_trs = "constant";
4366  uint16_t nb_coeff_model_trs = 1;
4367  FLTNB coeff_model_trs = 0.;
4368  string focal_model_axl = "constant";
4369  uint16_t nb_coeff_model_axl = 1;
4370  FLTNB coeff_model_axl = 0.;
4371 
4372  // others
4373  uint32_t voxels_number_trs;
4374  uint32_t voxels_number_axl;
4375  double fov_trs;
4376  double fov_axl;
4377 
4378  vector<string> path_mac_files;
4379  path_mac_files.push_back(a_pathMac);
4380 
4381  // Recover path to all macro files from the main mac file
4382  if(GetGATEMacFiles(a_pathMac , path_mac_files))
4383  {
4384  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()->Error occurred when trying to recover paths to GATE macro files !" << endl);
4385  return 1;
4386  }
4387 
4388 
4389  // Recover aliases of the different parts of the architecture
4390  if(GetGATEAliasesSPECT(path_mac_files, head_name, crystal_name, pixel_name, 2) )
4391  {
4392  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()->Error occurred when trying to recover aliases for the elements of the SPECThead !" << endl);
4393  return 1;
4394  }
4395 
4396  // Loop to recover all other informations
4397  for(uint16_t f=0 ; f<path_mac_files.size() ; f++)
4398  {
4399  ifstream systemMac(path_mac_files[f].c_str(), ios::in);
4400 
4401  string line;
4402  while(getline(systemMac, line))
4403  {
4404  // Scanner
4405  modality = "SPECT_CONVERGENT";
4406  vector <string> values;
4407  string entry = "";
4408 
4409  // assumes that first block is positionned on the x-axis
4410  entry = "/gate/"+head_name+"/placement/setTranslation";
4411  values = CheckGATECommand(entry, line);
4412  if (values.size()>0)
4413  {
4414  if(ConvertFromString(values[0], &head_pos_X) ||
4415  ConvertFromString(values[1], &head_pos_Y) )
4416  {
4417  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4418  return 1;
4419  }
4420 
4421  // Check first block cartesian coordinates is 0 on X or Y axis
4422  // Throw error otherwise
4423  if(head_pos_X!=0 && head_pos_Y !=0)
4424  {
4425  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4426  Cerr(" Block cartesian coordinates on either the X or Y axis expected to be equal to 0 "<< endl);
4427  return 1;
4428  }
4429 
4430  // Get the axis on which the first rsector is positionned
4431  if(head_pos_Y!=0) is_head_Y_axis = true;
4432 
4433  scanner_radius = is_head_Y_axis ? abs(head_pos_Y) : abs(head_pos_X) ;
4434  }
4435 
4436 
4437  entry = "/gate/"+head_name+"/geometry/setZLength";
4438  values = CheckGATECommand(entry, line);
4439  if (values.size()>0)
4440  {
4441  if(ConvertFromString(values[0], &fov_axl))
4442  {
4443  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4444  return 1;
4445  }
4446  // 4mm voxels by default
4447  voxels_number_axl = fov_axl/4 + 1;
4448  }
4449 
4450 
4451  entry = "/gate/"+head_name+"/ring/setRepeatNumber";
4452  values = CheckGATECommand(entry, line);
4453  if (values.size()>0)
4454  {
4455  if(ConvertFromString(values[0], &number_of_heads))
4456  {
4457  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4458  return 1;
4459  }
4460  }
4461 
4462 
4463  entry = "/gate/"+head_name+"/ring/setFirstAngle";
4464  values = CheckGATECommand(entry, line);
4465  if (values.size()>0)
4466  {
4467  if(ConvertFromString(values[0], &head_first_angle))
4468  {
4469  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4470  return 1;
4471  }
4472  }
4473 
4474 
4475  entry = "/gate/"+head_name+"/ring/setAngularPitch";
4476  values = CheckGATECommand(entry, line);
4477  if (values.size()>0)
4478  {
4479  if(ConvertFromString(values[0], &head_angular_pitch))
4480  {
4481  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4482  return 1;
4483  }
4484  }
4485 
4486  entry = "/gate/"+head_name+"/moves/insert";
4487  values = CheckGATECommand(entry, line);
4488  if (values.size()>0)
4489  {
4490  if(ConvertFromString(values[0], &head_orbit_name))
4491  {
4492  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4493  return 1;
4494  }
4495  }
4496 
4497  entry = "/gate/"+head_orbit_name+"/setSpeed";
4498  values = CheckGATECommand(entry, line);
4499  if (values.size()>0)
4500  {
4501  if(ConvertFromString(values[0], &head_rotation_speed))
4502  {
4503  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4504  return 1;
4505  }
4506  }
4507 
4508 
4509 
4510  // --- Crystals ---
4511  entry = "/gate/"+crystal_name+"/geometry/setXLength";
4512  values = CheckGATECommand(entry, line);
4513  if (values.size()>0)
4514  {
4515  double x_length;
4516  if(ConvertFromString(values[0], &x_length) )
4517  {
4518  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4519  return 1;
4520  }
4521 
4522  if (is_head_Y_axis)
4523  crystal_size_trs = x_length;
4524  else
4525  crystal_depth = x_length;
4526  }
4527 
4528  entry = "/gate/"+crystal_name+"/geometry/setYLength";
4529  values = CheckGATECommand(entry, line);
4530  if (values.size()>0)
4531  {
4532  double y_length;
4533  if(ConvertFromString(values[0], &y_length) )
4534  {
4535  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4536  return 1;
4537  }
4538 
4539  if (is_head_Y_axis)
4540  crystal_depth = y_length;
4541  else
4542  crystal_size_trs = y_length;
4543 
4544  }
4545 
4546  entry = "/gate/"+crystal_name+"/geometry/setZLength";
4547  values = CheckGATECommand(entry, line);
4548  if (values.size()>0)
4549  {
4550  if(ConvertFromString(values[0], &crystal_size_axl) )
4551  {
4552  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4553  return 1;
4554  }
4555  }
4556 
4557 
4558 
4559 
4560 
4561  // --- Pixels ---
4562  entry = is_head_Y_axis ?
4563  "/gate/"+pixel_name+"/cubicArray/setRepeatNumberX":
4564  "/gate/"+pixel_name+"/cubicArray/setRepeatNumberY";
4565 
4566 
4567  values = CheckGATECommand(entry, line);
4568  if (values.size()>0)
4569  {
4570  if(ConvertFromString(values[0], &number_of_pixels_trs) )
4571  {
4572  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4573  return 1;
4574  }
4575  }
4576 
4577  entry = "/gate/"+pixel_name+"/cubicArray/setRepeatNumberZ";
4578  values = CheckGATECommand(entry, line);
4579  if (values.size()>0)
4580  {
4581  if(ConvertFromString(values[0], &number_of_pixels_axl) )
4582  {
4583  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4584  return 1;
4585  }
4586  }
4587 
4588 
4589  entry = "/gate/"+pixel_name+"/geometry/setXLength";
4590  values = CheckGATECommand(entry, line);
4591  if (values.size()>0)
4592  {
4593  double x_length;
4594  if(ConvertFromString(values[0], &x_length) )
4595  {
4596  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4597  return 1;
4598  }
4599 
4600  if (is_head_Y_axis)
4601  pix_size_trs = x_length;
4602  }
4603 
4604  entry = "/gate/"+pixel_name+"/geometry/setYLength";
4605  values = CheckGATECommand(entry, line);
4606  if (values.size()>0)
4607  {
4608  double y_length;
4609  if(ConvertFromString(values[0], &y_length) )
4610  {
4611  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4612  return 1;
4613  }
4614 
4615  if (!is_head_Y_axis)
4616  pix_size_trs = y_length;
4617 
4618  }
4619 
4620  entry = "/gate/"+pixel_name+"/geometry/setZLength";
4621  values = CheckGATECommand(entry, line);
4622  if (values.size()>0)
4623  {
4624  if(ConvertFromString(values[0], &pix_size_axl) )
4625  {
4626  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4627  return 1;
4628  }
4629  }
4630 
4631 
4632  entry = "/gate/"+pixel_name+"/cubicArray/setRepeatVector";
4633  values = CheckGATECommand(entry, line);
4634  if (values.size()>0)
4635  {
4636  string trs_step = is_head_Y_axis ?
4637  values[0] :
4638  values[1] ;
4639 
4640  if(ConvertFromString(trs_step, &pix_step_trs) ||
4641  ConvertFromString(values[2], &pix_step_axl) )
4642  {
4643  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4644  return 1;
4645  }
4646  }
4647 
4648 
4649  // collimator parameter
4650  entry = is_head_Y_axis ?
4651  "/gate/fanbeam/geometry/setFocalDistanceY":
4652  "/gate/fanbeam/geometry/setFocalDistanceX";
4653 
4654  entry = "/gate/fanbeam/geometry/setFocalDistanceX";
4655  values = CheckGATECommand(entry, line);
4656  if (values.size()>0)
4657  {
4658  if(ConvertFromString(values[0], &coeff_model_trs) )
4659  {
4660  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4661  return 1;
4662  }
4663 
4664  focal_model_trs = "polynomial";
4665  }
4666 
4667  }
4668  systemMac.close();
4669  }
4670 
4671  // Transaxial FOV defined as scanner radius / 2
4672  fov_trs = scanner_radius/2;
4673  // 4mm voxels by default
4674  voxels_number_trs = fov_trs/2 + 1;
4675 
4676  uint32_t nb_pixels = number_of_pixels_axl * number_of_pixels_trs;
4677 
4678  pix_size_axl = nb_pixels>1 ? pix_size_axl : crystal_size_axl;
4679  pix_size_trs = nb_pixels>1 ? pix_size_trs : crystal_size_trs;
4680 
4681  // Compute gaps
4682  if (pix_step_axl - pix_size_axl >= 0)
4683  pix_gap_axl = pix_step_axl - pix_size_axl;
4684 
4685  if (pix_step_trs - pix_size_trs >= 0)
4686  pix_gap_trs = pix_step_trs - pix_size_trs;
4687 
4688 
4689  // CASToR x and y axis for rotation inverted in comparison with GATE
4690  head_first_angle = round(atan2f(head_pos_X , head_pos_Y) * 180. / M_PI)
4691  - head_first_angle;
4692 
4693 
4694  // Writing the .geom file
4695  ofstream fileGeom(a_pathGeom.c_str(), ios::out | ios::trunc);
4696 
4697  if(fileGeom)
4698  {
4699  fileGeom << "modality : " << modality << endl;
4700  fileGeom << "scanner name : " << scanner_name << endl;
4701  fileGeom << "number of detector heads : " << number_of_heads << endl;
4702  fileGeom << "trans number of pixels : " << number_of_pixels_trs << endl;
4703  fileGeom << "trans pixel size : " << pix_size_trs << endl;
4704  fileGeom << "trans gap size : " << pix_gap_trs << endl;
4705  fileGeom << "axial number of pixels : " << number_of_pixels_axl << endl;
4706  fileGeom << "axial pixel size : " << pix_size_axl << endl;
4707  fileGeom << "axial gap size : " << pix_gap_axl << endl;
4708 
4709  fileGeom << "detector depth : " << crystal_depth << endl;
4710 
4711  fileGeom << "scanner radius : " << scanner_radius;
4712  for(size_t h=1 ; h<number_of_heads ; h++)
4713  fileGeom << "," << scanner_radius;
4714  fileGeom << endl;
4715 
4716  fileGeom << "# Collimator configuration : "<< endl << endl;
4717  for(size_t h=0 ; h<number_of_heads ; h++)
4718  {
4719  fileGeom << "head" << h+1 << ":" << endl;
4720  fileGeom << "trans focal model: " << focal_model_trs << endl;
4721  fileGeom << "trans number of coef model: " << nb_coeff_model_trs << endl;
4722  fileGeom << "trans parameters: " << coeff_model_trs << endl;
4723  fileGeom << "axial focal model: " << focal_model_axl << endl;
4724  fileGeom << "axial number of coef model: " << nb_coeff_model_axl << endl;
4725  fileGeom << "axial parameters: " << coeff_model_axl << endl;
4726  fileGeom << endl;
4727  }
4728 
4729  fileGeom << "" << endl;
4730  fileGeom << "# default reconstruction parameters" << endl;
4731  fileGeom << "voxels number transaxial : " << voxels_number_trs << endl;
4732  fileGeom << "voxels number axial : " << voxels_number_axl << endl;
4733 
4734  fileGeom << "field of view transaxial : " << fov_trs << endl;
4735  fileGeom << "field of view axial : " << fov_axl << endl << endl ;
4736  fileGeom << ""<< endl;
4737 
4738  fileGeom << "# description" << endl;
4739  fileGeom << "description : " << description << endl;
4740 
4741  fileGeom.close();
4742 
4743  Cout("Output geom file written at :" << a_pathGeom << endl);
4744  }
4745  else
4746  {
4747  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Couldn't open geom file for writing "<< a_pathGeom << " !" << endl);
4748  return 1;
4749  }
4750 
4751  return 0;
4752 }
This header file is mainly used to declare some macro definitions and all includes needed from the st...
uint32_t ConvertIDecat(int32_t nBlocksPerRing, int32_t nBlocksLine, int32_t nCrystalsTransaxial, int32_t nCrystalsAxial, int32_t crystalID, int32_t blockID)
Compute a CASToR crystal index of a GATE ecat system from its indexes (block/crystal) and the system ...
#define GATE_SYS_UNKNOWN
#define KIND_RDM
#define FLTNB
Definition: gVariables.hh:81
This file gathers various function dedicated to data conversion in order to convert various type of G...
int IntfKeyGetRecurringValueFromFile(const string &a_pathToHeader, const string &a_key, T *ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences)
#define GATE_SYS_ECAT
#define GEO_ROT_CCW
Definition: vScanner.hh:48
#define KIND_TRUE
vector< string > CheckGATECommand(const string &a_key, const string &a_line)
Check if the line contains the provided GATE command. In this case, parse the line and returns the va...
int GetGATEAliasesCylindrical(vector< string > path_mac_files, string &rsector_name, string &module_name, string &submodule_name, string &crystal_name, vector< string > &layers_name, int vb)
Loop over a list of path to GATE macro files passed in parameter to recover aliases of the different ...
int IntfKeyGetValueFromFile(const string &a_pathToHeader, const string &a_key, T *ap_return, int a_nbElts, int a_mandatoryFlag)
Look for "a_nbElts" elts in the "a_pathToHeader" interfile header matching the "a_keyword" key passed...
Definition: oInterfileIO.cc:61
#define GATE_SYS_SPECT
int ReadMacECAT(string a_pathMac, uint32_t &nCrystalsTot, uint32_t &nCrystalsAxial, uint32_t &nCrystalsTransaxial, uint32_t &nBlocksLine, uint32_t &nBlocksPerRing, uint32_t &start_time_ms, uint32_t &duration_ms, FLTNB &pet_coinc_window, int vb)
#define KIND_UNKNOWN
string GetFileFromPath(const string &a_pathToFile)
Simply return the file from a path string passed in parameter.
Definition: gOptions.cc:1152
int ConvertFromString(const string &a_str, string *a_result)
Copy the &#39;a_str&#39; string in the position pointed by &#39;a_result&#39;.
Definition: gOptions.cc:782
int GetGATEMacFiles(const string &a_pathMac, vector< string > &ap_pathToMacFiles)
Extract the paths to each macro file contained in the main macro file.
#define Cerr(MESSAGE)
#define KIND_SCAT
int CreateGeomWithSPECT(string a_pathMac, string a_pathGeom)
Read a GATE macro file containing the description of a SPECThead system, and convert it to a geom fil...
int ReadIntfSPECT(string a_pathIntf, float_t &a_distToDetector, uint32_t &a_nHeads, uint32_t &a_nPixAxl, uint32_t &a_nPixTrs, float_t &a_crystalSizeAxl, float_t &a_crystalSizeTrs, uint32_t &a_nProjectionsTot, uint32_t &a_nProjectionsByHead, float_t &a_head1stAngle, float_t &a_headAngPitchDeg, float_t &a_headAngStepDeg, int &a_headRotDirection, uint32_t &a_start_time_ms, uint32_t &a_duration_ms, int vb)
Recover informations about the scanner element of an ECAT system, and acquisition duration...
void ConvertValuesTomm(vector< string > &ap_v)
Check if the vector of strings passed in parameter contains the &#39;cm&#39; unit In this case...
uint32_t ConvertIDcylindrical(uint32_t nRsectorsAngPos, uint32_t nRsectorsAxial, bool a_invertDetOrder, int a_rsectorIdOrder, uint32_t nModulesTransaxial, uint32_t nModulesAxial, uint32_t nSubmodulesTransaxial, uint32_t nSubmodulesAxial, uint32_t nCrystalsTransaxial, uint32_t nCrystalsAxial, uint8_t nLayers, uint32_t *nCrystalPerLayer, vector< uint32_t > nLayersRptTransaxial, vector< uint32_t > nLayersRptAxial, int32_t layerID, int32_t crystalID, int32_t submoduleID, int32_t moduleID, int32_t rsectorID)
Compute a CASToR crystal index of a GATE cylindricalPET system from its indexes (rsector/module/submo...
int ReadMacSPECT(string a_pathMac, float_t &a_distToDetector, uint32_t &a_nHeads, uint32_t &a_nPixAxl, uint32_t &a_nPixTrs, float_t &a_crystalSizeAxl, float_t &a_crystalSizeTrs, uint32_t &a_nProjectionsTot, uint32_t &a_nProjectionsByHead, float_t &a_head1stAngle, float_t &a_headAngPitch, float_t &a_headAngStepDeg, int &a_headRotDirection, uint32_t &a_start_time_ms, uint32_t &a_duration_ms, int vb)
Recover informations about the scanner element of an ECAT system, and acquisition duration...
int ComputeKindGATEEvent(uint32_t eventID1, uint32_t eventID2, int comptonPhantom1, int comptonPhantom2, int rayleighPhantom1, int rayleighPhantom2)
Determine kind of a given coincidence event, from its attributes.
#define KEYWORD_MANDATORY
Definition: gOptions.hh:47
#define KEYWORD_OPTIONAL
Definition: gOptions.hh:49
#define GATE_SYS_CYLINDRICAL
int GetGATEAliasesEcat(vector< string > path_mac_files, string &block_name, string &crystal_name, int vb)
Loop over a list of path to GATE macro files passed in parameter to recover aliases of the different ...
uint32_t ConvertIDSPECTRoot2(uint32_t a_nbSimulatedPixels, uint32_t a_nPixTrs, uint32_t a_nPixAxl, int32_t a_headID, int32_t a_crystalID, int32_t a_pixelID, float_t a_rotAngle, float_t a_headAngPitch, float_t a_crystalSizeAxl, float_t a_crystalSizeTrs, float_t a_gPosX, float_t a_gPosY, float_t a_gPosZ)
Compute a CASToR crystal index of a GATE SPECThead system.
string toString(T a_val)
Convert a value of any type into string.
string GetPathOfFile(const string &a_pathToFile)
Simply return the path to the directory of a file path string passed in parameter.
Definition: gOptions.cc:1177
int CreateGeomWithCylindrical(string a_pathMac, string a_pathGeom)
Read a GATE macro file containing the description of a cylindricalPET system, and convert it to a geo...
uint32_t ConvertIDSPECTRoot1(int32_t a_headID, float_t a_rotAngle, float_t a_angStep, uint32_t a_nProjectionsByHead)
Compute a CASToR projection index of a GATE SPECThead system.
#define GATE_NB_MAX_LAYERS
int CreateGeomWithECAT(string a_pathMac, string a_pathGeom)
Read a GATE macro file containing the description of an ecat system, and convert it to a geom file...
#define KIND_MSCAT
vector< string > Split(string a_line)
Split the line provided in parameter into a vector of strings (separator is blankspace) ...
#define Cout(MESSAGE)
int WriteVector(ofstream &file, const string &a_key, vector< T > a_vals)
Write the key and its values in the file provided in parameter.
int ReadMacCylindrical(string a_pathMac, uint8_t &nLayers, uint32_t *nb_crystal_per_layer, uint32_t &nCrystalsTot, uint32_t &nCrystalsAxial, uint32_t &nCrystalsTransaxial, vector< uint32_t > &nLayersRptAxial, vector< uint32_t > &nLayersRptTransaxial, uint32_t &nSubmodulesAxial, uint32_t &nSubmodulesTransaxial, uint32_t &nModulesAxial, uint32_t &nModulesTransaxial, uint32_t &nRsectorsAxial, uint32_t &nRsectorsAngPos, bool &invert_det_order, int &rsector_id_order, uint32_t &start_time_ms, uint32_t &duration_ms, FLTNB &pet_coinc_window, int vb)
#define GEO_ROT_CW
Definition: vScanner.hh:46
int GetGATESystemType(const string &a_pathMac)
Read a GATE macro file and identify the system type from the &#39;gate/systems/&#39; command lines...
int GetGATEAliasesSPECT(vector< string > path_mac_files, string &base_name, string &crystal_name, string &pixel_name, int vb)
Loop over a list of path to GATE macro files passed in parameter to recover aliases of the different ...