CASToR  3.0
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-2019 all CASToR contributors listed below:
18 
19  --> Didier BENOIT, Claude COMTAT, Marina FILIPOVIC, Thibaut MERLIN, Mael MILLARDET, Simon STUTE, Valentin VIELZEUF
20 
21 This is CASToR version 3.0.
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 vb : verbosity
1077  \brief Recover informations about the scanner element of a cylindricalPET system and acquisition duration, from a GATE macro file
1078  \return 0 if success, positive value otherwise
1079 */
1080 int ReadMacCylindrical(string a_pathMac,
1081  uint8_t &nLayers,
1082  uint32_t *nb_crystal_per_layer,
1083  uint32_t &nCrystalsTot,
1084  uint32_t &nCrystalsAxial,
1085  uint32_t &nCrystalsTransaxial,
1086  vector<uint32_t> &nLayersRptAxial,
1087  vector<uint32_t> &nLayersRptTransaxial,
1088  uint32_t &nSubmodulesAxial,
1089  uint32_t &nSubmodulesTransaxial,
1090  uint32_t &nModulesAxial,
1091  uint32_t &nModulesTransaxial,
1092  uint32_t &nRsectorsAxial,
1093  uint32_t &nRsectorsAngPos,
1094  bool &invert_det_order,
1095  int &rsector_id_order,
1096  uint32_t &start_time_ms,
1097  uint32_t &duration_ms,
1098  int vb)
1099 {
1100  vector<string> path_mac_files;
1101  path_mac_files.push_back(a_pathMac);
1102 
1103  // Recover path to all macro files from the main mac file
1104  if(GetGATEMacFiles(a_pathMac , path_mac_files))
1105  {
1106  Cerr("***** GetGATESystemType() ->Error occurred when trying to recover paths to GATE macro files !" << endl);
1107  return 1;
1108  }
1109 
1110  string rsector_name = "";
1111  string module_name = "";
1112  string submodule_name = "";
1113  string crystal_name = "";
1114  string mod_rptr_type = "cubicArray";
1115  string smod_rptr_type = "cubicArray";
1116  string cry_rptr_type = "cubicArray";
1117  string lay_rptr_type = "cubicArray";
1118 
1119  vector <string> layers_name;
1120  bool is_rsector_Y_axis = false;
1121 
1122  // Recover aliases of the different parts of the architecture
1123  if(GetGATEAliasesCylindrical(path_mac_files, rsector_name, module_name, submodule_name, crystal_name, layers_name, vb) )
1124  {
1125  Cerr("***** GetGATESystemType() ->Error occurred when trying to recover aliases for the elements of the cylindricalPET !" << endl);
1126  return 1;
1127  }
1128 
1129  // Recover nb of detected layers
1130  nLayers = layers_name.size();
1131 
1132  // Loop to recover all other informations
1133  for(uint16_t f=0 ; f<path_mac_files.size() ; f++)
1134  {
1135  ifstream mac_file(path_mac_files[f].c_str(), ios::in);
1136 
1137  string line;
1138  double time_start =-1.,
1139  time_stop =-1.,
1140  time_slices =-1.;
1141 
1142  while(getline(mac_file, line))
1143  {
1144  vector <string> values;
1145  string kword ="";
1146 
1147 
1148  // RSECTORS
1149 
1150  kword = "/gate/"+rsector_name+"/placement/setTranslation";
1151  values = CheckGATECommand(kword, line);
1152 
1153  // Check where the first rsector has been created
1154  if (values.size()>0)
1155  {
1156  FLTNB rsector_pos_X =0.,
1157  rsector_pos_Y =0.;
1158 
1159  if(ConvertFromString(values[0], &rsector_pos_X) ||
1160  ConvertFromString(values[1], &rsector_pos_Y) )
1161  {
1162  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
1163  return 1;
1164  }
1165 
1166  // Get the axis on which the first rsector is positionned
1167  if(rsector_pos_Y!=0) is_rsector_Y_axis = true;
1168 
1169  // Get the detector ordering depending on the position of the first rsector
1170  // (inverted if located on top (Y-axis with positive value)
1171  // or on the X axis with negative value)
1172 
1173  if(rsector_pos_Y > 0 || rsector_pos_X < 0)
1174  invert_det_order = true;
1175 
1176  }
1177 
1178 
1179  kword = "/gate/"+rsector_name+"/ring/setRepeatNumber";
1180  values = CheckGATECommand(kword, line);
1181  if (values.size()>0)
1182  {
1183  if(ConvertFromString( values[0] , &nRsectorsAngPos) )
1184  {
1185  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1186  return 1;
1187  }
1188  }
1189 
1190 
1191  // Check if (axial) linear repeaters for rsectors
1192  kword = "/gate/"+rsector_name+"/linear/setRepeatNumber";
1193  values = CheckGATECommand(kword, line);
1194  if (values.size()>0)
1195  {
1196  if(ConvertFromString(values[0], &nRsectorsAxial) )
1197  {
1198  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
1199  return 1;
1200  }
1201  }
1202 
1203 
1204  // Check if cubic array repeaters for rsectors
1205  kword = "/gate/"+rsector_name+"/cubicArray/setRepeatNumberZ";
1206  values = CheckGATECommand(kword, line);
1207 
1208  if (values.size()>0)
1209  {
1210  // Using a cubic array after a ring repeater (nRsectorsAngPos>1) leads to rsector being ordered axially first (and not transaxially)
1211  // Track this using a flag
1212  rsector_id_order = nRsectorsAngPos>1 ? 1 : 0 ;
1213 
1214  if(ConvertFromString(values[0], &nRsectorsAxial) )
1215  {
1216  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
1217  return 1;
1218  }
1219  }
1220 
1221 
1222 
1223 
1224  // MODULES
1225 
1226  // Box/cubicArray repeaters
1227  kword = is_rsector_Y_axis ?
1228  "/gate/"+module_name+"/cubicArray/setRepeatNumberX":
1229  "/gate/"+module_name+"/cubicArray/setRepeatNumberY";
1230 
1231  values = CheckGATECommand(kword, line);
1232  if (values.size()>0)
1233  {
1234  if(ConvertFromString( values[0] , &nModulesTransaxial) )
1235  {
1236  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1237  return 1;
1238  }
1239  }
1240 
1241  kword = "/gate/"+module_name+"/cubicArray/setRepeatNumberZ";
1242  values = CheckGATECommand(kword, line);
1243  if (values.size()>0)
1244  {
1245  if(ConvertFromString( values[0] , &nModulesAxial) )
1246  {
1247  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1248  return 1;
1249  }
1250  }
1251 
1252  // linear repeaters instead of box
1253  kword = "/gate/"+module_name+"/linear/setRepeatNumber";
1254  values = CheckGATECommand(kword, line);
1255  if (values.size()>0)
1256  {
1257  if(ConvertFromString( values[0] , &nModulesAxial) )
1258  {
1259  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1260  return 1;
1261  }
1262  }
1263 
1264 
1265  // SUBMODULES
1266 
1267  kword = is_rsector_Y_axis ?
1268  "/gate/"+submodule_name+"/cubicArray/setRepeatNumberX":
1269  "/gate/"+submodule_name+"/cubicArray/setRepeatNumberY";
1270 
1271  values = CheckGATECommand(kword, line);
1272  if (values.size()>0)
1273  {
1274  if(ConvertFromString( values[0] , &nSubmodulesTransaxial) )
1275  {
1276  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1277  return 1;
1278  }
1279  }
1280 
1281  kword = "/gate/"+submodule_name+"/cubicArray/setRepeatNumberZ";
1282  values = CheckGATECommand(kword, line);
1283  if (values.size()>0)
1284  {
1285  if(ConvertFromString( values[0] , &nSubmodulesAxial) )
1286  {
1287  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1288  return 1;
1289  }
1290  }
1291 
1292  // linear repeaters instead of box
1293  kword = "/gate/"+submodule_name+"/linear/setRepeatNumber";
1294  values = CheckGATECommand(kword, line);
1295  if (values.size()>0)
1296  {
1297  if(ConvertFromString( values[0] , &nSubmodulesAxial) )
1298  {
1299  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1300  return 1;
1301  }
1302  }
1303 
1304 
1305 
1306  // CRYSTALS
1307 
1308  kword = is_rsector_Y_axis ?
1309  "/gate/"+crystal_name+"/cubicArray/setRepeatNumberX":
1310  "/gate/"+crystal_name+"/cubicArray/setRepeatNumberY";
1311 
1312  values = CheckGATECommand(kword, line);
1313  if (values.size()>0)
1314  {
1315  if(ConvertFromString( values[0] , &nCrystalsTransaxial) )
1316  {
1317  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1318  return 1;
1319  }
1320  }
1321 
1322  kword = "/gate/"+crystal_name+"/cubicArray/setRepeatNumberZ";
1323  values = CheckGATECommand(kword, line);
1324  if (values.size()>0)
1325  {
1326  if(ConvertFromString( values[0] , &nCrystalsAxial) )
1327  {
1328  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1329  return 1;
1330  }
1331  }
1332 
1333 
1334  // linear repeaters instead of box
1335  kword = "/gate/"+crystal_name+"/linear/setRepeatNumber";
1336  values = CheckGATECommand(kword, line);
1337  if (values.size()>0)
1338  {
1339  if(ConvertFromString( values[0] , &nCrystalsAxial) )
1340  {
1341  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1342  return 1;
1343  }
1344  }
1345 
1346 
1347  // LAYERS
1348 
1349  // Check if there are any repeaters on crystal layers
1350  for(int l=0 ; l<nLayers ; l++)
1351  {
1352  kword = is_rsector_Y_axis ?
1353  "/gate/"+layers_name[l]+"/cubicArray/setRepeatNumberX":
1354  "/gate/"+layers_name[l]+"/cubicArray/setRepeatNumberY";
1355 
1356  values = CheckGATECommand(kword, line);
1357  if (values.size()>0)
1358  {
1359  int32_t val;
1360  if(ConvertFromString( values[0] , &val) )
1361  {
1362  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1363  return 1;
1364  }
1365  nLayersRptTransaxial.push_back(val);
1366  }
1367 
1368  kword = "/gate/"+layers_name[l]+"/cubicArray/setRepeatNumberZ";
1369  values = CheckGATECommand(kword, line);
1370  if (values.size()>0)
1371  {
1372  int32_t val;
1373  if(ConvertFromString( values[0] , &val) )
1374  {
1375  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1376  return 1;
1377  }
1378  nLayersRptAxial.push_back(val);
1379  }
1380 
1381  // linear repeaters instead of box
1382  kword = "/gate/"+layers_name[l]+"/linear/setRepeatNumber";
1383  values = CheckGATECommand(kword, line);
1384  if (values.size()>0)
1385  {
1386  int32_t val;
1387  if(ConvertFromString( values[0] , &val) )
1388  {
1389  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1390  return 1;
1391  }
1392  nLayersRptAxial.push_back(val);
1393  }
1394 
1395 
1396  }
1397 
1398 
1399  kword = "/gate/application/setTimeStart";
1400  values = CheckGATECommand(kword, line);
1401  if (values.size()>0)
1402  {
1403  if(ConvertFromString( values[0] , &time_start) )
1404  {
1405  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1406  return 1;
1407  }
1408 
1409  // Convert in ms
1410  if (values.size()>1)
1411  {
1412  if(values[1] == "s") time_start *= 1000;
1413  }
1414  else
1415  Cerr("***** dataConversionUtilities::readMacCylindrical()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
1416 
1417  start_time_ms = time_start;
1418  }
1419 
1420  kword = "/gate/application/setTimeStop";
1421  values = CheckGATECommand(kword, line);
1422  if (values.size()>0)
1423  {
1424  if(ConvertFromString( values[0] , &time_stop) )
1425  {
1426  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1427  return 1;
1428  }
1429 
1430  // Convert in ms
1431  if (values.size()>1)
1432  {
1433  if(values[1] == "s") time_stop *= 1000;
1434  }
1435  else
1436  Cerr("***** dataConversionUtilities::readMacCylindrical()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
1437  }
1438 
1439  kword = "/gate/application/addSlice";
1440  values = CheckGATECommand(kword, line);
1441  if (values.size()>0)
1442  {
1443  double time_slice_tmp=0;
1444 
1445  if(ConvertFromString( values[0] , &time_slice_tmp) )
1446  {
1447  Cerr("***** dataConversionUtilities::readMacCylindrical()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1448  return 1;
1449  }
1450 
1451  // Convert in ms
1452  if (values.size()>1)
1453  {
1454  if(values[1] == "s") time_slice_tmp *= 1000;
1455  }
1456  else
1457  Cerr("***** dataConversionUtilities::readMacCylindrical()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
1458 
1459  time_slices += time_slice_tmp;
1460  }
1461  }
1462 
1463  // Compute duration in ms
1464  duration_ms = (time_slices>0) ?
1465  (uint32_t)(time_slices-time_start) :
1466  duration_ms;
1467 
1468  duration_ms = (time_start>=0 && time_stop>=0) ?
1469  (uint32_t)(time_stop-time_start) :
1470  duration_ms ;
1471 
1472  mac_file.close();
1473  }
1474 
1475  // Computing the total number of crystals in the scanner
1476  if(nLayers == 0)
1477  {
1478  nCrystalsTot = nRsectorsAngPos * nRsectorsAxial
1479  * nModulesTransaxial * nModulesAxial
1480  * nSubmodulesTransaxial * nSubmodulesAxial
1481  * nCrystalsTransaxial * nCrystalsAxial;
1482  }
1483  else
1484  for(int l=0 ; l<nLayers ; l++)
1485  {
1486  uint32_t nb_crystals_layer = nRsectorsAngPos * nRsectorsAxial
1487  * nModulesTransaxial * nModulesAxial
1488  * nSubmodulesTransaxial * nSubmodulesAxial
1489  * nCrystalsTransaxial * nCrystalsAxial;
1490 
1491  // Add layer elements if repeaters have been used on layers
1492  if(nLayersRptTransaxial.size()>0 || nLayersRptAxial.size()>0 )
1493  nb_crystals_layer *= nLayersRptTransaxial[l] * nLayersRptAxial[l];
1494 
1495  nb_crystal_per_layer[l] = nb_crystals_layer;
1496 
1497  nCrystalsTot += nb_crystals_layer;
1498  }
1499 
1500 
1501  if(vb >= 2)
1502  {
1503  Cout(endl);
1504  Cout("-----------------------------------------------------------" << endl);
1505  Cout("ReadMacCylindrical()-> Information recovered from mac file:" << endl);
1506  Cout("-----------------------------------------------------------" << endl);
1507  Cout("Number of rsectors angular position: " << nRsectorsAngPos << endl);
1508  Cout("Number of axial rsectors: " << nRsectorsAxial << endl);
1509  Cout("Number of axial modules: " << nModulesAxial << endl);
1510  Cout("Number of transaxial modules: " << nModulesTransaxial << endl);
1511  Cout("Number of axial submodules: " << nSubmodulesAxial << endl);
1512  Cout("Number of transaxial submodules: " << nSubmodulesTransaxial << endl);
1513  Cout("Number of axial crystals: " << nCrystalsAxial << endl);
1514  Cout("Number of transaxial crystals: " << nCrystalsTransaxial << endl);
1515  if (nLayers>=1)
1516  {
1517  Cout("Number of layers: " << (uint16_t)nLayers << endl); // cast to uint16_t for output purposes
1518  for(int l=0 ; l<nLayers ; l++)
1519  Cout("Layer "<< l <<" : Number of crystals: " << nb_crystal_per_layer[l] << endl);
1520  }
1521  Cout("Total number of crystals (including layers): " << nCrystalsTot << endl);
1522  Cout("Acquisition start time (ms): " << start_time_ms << endl);
1523  Cout("Acquisition duration (ms): " << duration_ms << endl);
1524  Cout("-----------------------------------------------------------" << endl << endl);
1525  }
1526 
1527  return 0;
1528 }
1529 
1530 
1531 
1532 // =====================================================================
1533 // ---------------------------------------------------------------------
1534 // ---------------------------------------------------------------------
1535 // =====================================================================
1536 /*
1537  \fn ReadMacECAT
1538  \param a_pathMac : path to a macro file
1539  \param nCrystalsAxial : nb of axial crystals
1540  \param nCrystalsTransaxial : nb of transaxial crystals
1541  \param nBlocksLine : nb of axial blocks
1542  \param nBlocksPerRing : nb of blocks per ring
1543  \param start_time_ms : acquisition start time converted in ms
1544  \param duration_ms : acquisition duration converted in ms
1545  \param vb : verbosity
1546  \brief Recover informations about the scanner element of an ECAT system, and acquisition duration, from a GATE macro file
1547  \return 0 if success, positive value otherwise
1548 */
1549 int ReadMacECAT( string a_pathMac,
1550  uint32_t &nCrystalsTot,
1551  uint32_t &nCrystalsAxial,
1552  uint32_t &nCrystalsTransaxial,
1553  uint32_t &nBlocksLine,
1554  uint32_t &nBlocksPerRing,
1555  uint32_t &start_time_ms,
1556  uint32_t &duration_ms,
1557  int vb)
1558 {
1559  // Recover path to all macro files from the main mac file
1560  vector<string> path_mac_files;
1561  path_mac_files.push_back(a_pathMac);
1562  if(GetGATEMacFiles(a_pathMac , path_mac_files))
1563  {
1564  Cerr("***** GetGATESystemType() ->Error occurred when trying to recover paths to GATE macro files !" << endl);
1565  return 1;
1566  }
1567 
1568  string block_name = "block";
1569  string crystal_name = "crystal";
1570  bool is_block_Y_axis = false;
1571 
1572  // Recover aliases of the different parts of the architecture
1573  if(GetGATEAliasesEcat(path_mac_files, block_name, crystal_name, vb) )
1574  {
1575  Cerr("***** GetGATESystemType() ->Error occurred when trying to recover aliases for the elements of the ecat !" << endl);
1576  return 1;
1577  }
1578 
1579 
1580  // 2nd loop to recover all other informations
1581  for(uint16_t f=0 ; f<path_mac_files.size() ; f++)
1582  {
1583  ifstream mac_file(path_mac_files[f].c_str(), ios::in);
1584 
1585  string line;
1586  double time_start=-1.,
1587  time_stop=-1.,
1588  time_slices=-1.;
1589 
1590  while(getline(mac_file, line))
1591  {
1592  vector <string> values;
1593  string kword ="";
1594 
1595  kword = "/gate/"+block_name+"/placement/setTranslation";
1596  values = CheckGATECommand(kword, line);
1597  if (values.size()>0)
1598  {
1599  FLTNB block_pos_X=0.,
1600  block_pos_Y=0.;
1601 
1602  if(ConvertFromString(values[0], &block_pos_X) ||
1603  ConvertFromString(values[1], &block_pos_Y) )
1604  {
1605  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
1606  return 1;
1607  }
1608 
1609  // Get the axis on which the first rsector is positionned
1610  if(block_pos_Y!=0) is_block_Y_axis = true;
1611  }
1612 
1613  kword = "/gate/"+block_name+"/ring/setRepeatNumber";
1614  values = CheckGATECommand(kword, line);
1615  if (values.size()>0)
1616  {
1617  if(ConvertFromString( values[0] , &nBlocksPerRing) )
1618  {
1619  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1620  return 1;
1621  }
1622  }
1623 
1624  kword = "/gate/"+block_name+"/linear/setRepeatNumber";
1625  values = CheckGATECommand(kword, line);
1626  if (values.size()>0)
1627  {
1628  if(ConvertFromString( values[0] , &nBlocksLine) )
1629  {
1630  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1631  return 1;
1632  }
1633  }
1634 
1635  kword = is_block_Y_axis ?
1636  "/gate/"+crystal_name+"/cubicArray/setRepeatNumberX":
1637  "/gate/"+crystal_name+"/cubicArray/setRepeatNumberY";
1638 
1639  values = CheckGATECommand(kword, line);
1640  if (values.size()>0)
1641  {
1642  if(ConvertFromString( values[0] , &nCrystalsTransaxial) )
1643  {
1644  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1645  return 1;
1646  }
1647  }
1648 
1649  kword = "/gate/"+crystal_name+"/cubicArray/setRepeatNumberZ";
1650  values = CheckGATECommand(kword, line);
1651  if (values.size()>0)
1652  {
1653  if(ConvertFromString( values[0] , &nCrystalsAxial) )
1654  {
1655  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1656  return 1;
1657  }
1658  }
1659 
1660 
1661  kword = "/gate/application/setTimeStart";
1662  values = CheckGATECommand(kword, line);
1663  if (values.size()>0)
1664  {
1665  if(ConvertFromString( values[0] , &time_start) )
1666  {
1667  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1668  return 1;
1669  }
1670 
1671  // Convert in ms
1672  if (values.size()>1)
1673  {
1674  if(values[1] == "s") time_start *= 1000;
1675  }
1676  else
1677  Cerr("***** dataConversionUtilities::readMacECAT()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
1678 
1679  start_time_ms = time_start;
1680  }
1681 
1682  kword = "/gate/application/setTimeStop";
1683  values = CheckGATECommand(kword, line);
1684  if (values.size()>0)
1685  {
1686  if(ConvertFromString( values[0] , &time_stop) )
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  // Convert in ms
1693  if (values.size()>1)
1694  {
1695  if(values[1] == "s") time_stop *= 1000;
1696  }
1697  else
1698  Cerr("***** dataConversionUtilities::readMacECAT()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
1699  }
1700 
1701  kword = "/gate/application/addSlice";
1702  values = CheckGATECommand(kword, line);
1703  if (values.size()>0)
1704  {
1705  double time_slice_tmp=0;
1706 
1707  if(ConvertFromString( values[0] , &time_slice_tmp) )
1708  {
1709  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1710  return 1;
1711  }
1712 
1713  // Convert in ms
1714  if (values.size()>1)
1715  {
1716  if(values[1] == "s") time_slice_tmp *= 1000;
1717  }
1718  else
1719  Cerr("***** dataConversionUtilities::readMacECAT()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
1720 
1721  time_slices = time_slice_tmp;
1722  }
1723  }
1724 
1725  // Compute duration in ms
1726  duration_ms = (time_slices>0) ?
1727  (uint32_t)(time_slices-time_start) :
1728  duration_ms;
1729 
1730  duration_ms = (time_start>=0 && time_stop>=0) ?
1731  (uint32_t)(time_stop-time_start) :
1732  duration_ms ;
1733 
1734  mac_file.close();
1735  }
1736 
1737  // Computing the total number of crystals in the scanner
1738  nCrystalsTot = nCrystalsTransaxial * nCrystalsAxial
1739  * nBlocksLine * nBlocksPerRing;
1740 
1741 
1742  if(vb >= 2)
1743  {
1744  Cout(endl);
1745  Cout("-----------------------------------------------------" << endl);
1746  Cout("ReadMacECAT()-> Information recovered from mac file:" << endl);
1747  Cout("-----------------------------------------------------" << endl);
1748  Cout("Number of blocks per ring: " << nBlocksPerRing << endl);
1749  Cout("Number of axial blocks: " << nBlocksLine << endl);
1750  Cout("Number of axial crystals: " << nCrystalsAxial << endl);
1751  Cout("Number of transaxial crystals: " << nCrystalsTransaxial << endl);
1752  Cout("Total number of crystals: " << nCrystalsTot << endl);
1753  Cout("Acquisition start time (ms): " << start_time_ms << endl);
1754  Cout("Acquisition duration (ms): " << duration_ms << endl);
1755  Cout("-----------------------------------------------------" << endl << endl);
1756  }
1757 
1758  return 0;
1759 }
1760 
1761 
1762 
1763 // =====================================================================
1764 // ---------------------------------------------------------------------
1765 // ---------------------------------------------------------------------
1766 // =====================================================================
1767 /*
1768  \fn ReadMacSPECT
1769  \param a_pathMac : path to a macro file
1770  \param distToDetector : distance between center of rotation and detector surface
1771  \param nHeads : nb of cameras
1772  \param nPixAxl : nb of transaxial pixels
1773  \param nPixTrs : nb of axial pixels
1774  \param crystalSizeAxl : crystal axial dimension
1775  \param crystalSizeTrs : crystal transaxial dimension
1776  \param head1stAngle : head first angle
1777  \param headAngPitch : angular pitch between heads
1778  \param headRotSpeed : angle between projection
1779  \param headRotDirection : rotation direction of the head (CW or CCW)
1780  \param start_time_ms : acquisition start time converted in ms
1781  \param duration_ms : acquisition duration converted in ms
1782  \param vb : verbosity
1783  \brief Recover informations about the scanner element of an ECAT system, and acquisition duration, from a GATE macro file
1784  \return 0 if success, positive value otherwise
1785 */
1786 int ReadMacSPECT( string a_pathMac,
1787  float_t &a_distToDetector,
1788  uint32_t &a_nHeads,
1789  uint32_t &a_nPixAxl,
1790  uint32_t &a_nPixTrs,
1791  float_t &a_crystalSizeAxl,
1792  float_t &a_crystalSizeTrs,
1793  uint32_t &a_nProjectionsTot,
1794  uint32_t &a_nProjectionsByHead,
1795  float_t &a_head1stAngle,
1796  float_t &a_headAngPitch,
1797  float_t &a_headAngStepDeg,
1798  int &a_headRotDirection,
1799  uint32_t &a_start_time_ms,
1800  uint32_t &a_duration_ms,
1801  int vb)
1802 {
1803  // Recover path to all macro files from the main mac file
1804  vector<string> path_mac_files;
1805  path_mac_files.push_back(a_pathMac);
1806 
1807  if(GetGATEMacFiles(a_pathMac , path_mac_files))
1808  {
1809  Cerr("***** GetGATESystemType() ->Error occurred when trying to recover paths to GATE macro files !" << endl);
1810  return 1;
1811  }
1812 
1813  string head_name = "SPECThead";
1814  string crystal_name = "crystal";
1815  string pixel_name = "pixel";
1816  string head_orbit_name = "";
1817  bool is_head_Y_axis = false;
1818 
1819  // Recover aliases of the different parts of the architecture
1820  if(GetGATEAliasesSPECT(path_mac_files, head_name, crystal_name, pixel_name, vb) )
1821  {
1822  Cerr("***** GetGATESystemType() ->Error occurred when trying to recover aliases for the elements of the ecat !" << endl);
1823  return 1;
1824  }
1825 
1826  // Init variables to recover some data
1827  double time_start=-1.,
1828  time_stop=-1.,
1829  time_slice=-1,
1830  time_slices=-1.,
1831  time_slice_ms=-1.,
1832  head_rot_speed =-1.;
1833 
1834  // 2nd loop to recover all other informations
1835  for(uint16_t f=0 ; f<path_mac_files.size() ; f++)
1836  {
1837  ifstream mac_file(path_mac_files[f].c_str(), ios::in);
1838 
1839  string line;
1840 
1841  while(getline(mac_file, line))
1842  {
1843  vector <string> values;
1844  string kword ="";
1845  kword = "/gate/"+head_name+"/placement/setTranslation";
1846  values = CheckGATECommand(kword, line);
1847  if (values.size()>0)
1848  {
1849  FLTNB head_pos_X=0.,
1850  head_pos_Y=0.;
1851 
1852  if(ConvertFromString(values[0], &head_pos_X) ||
1853  ConvertFromString(values[1], &head_pos_Y) )
1854  {
1855  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
1856  return 1;
1857  }
1858 
1859  // Get the axis on which the first rsector is positionned
1860  if(head_pos_Y!=0) is_head_Y_axis = true;
1861 
1862  a_distToDetector = is_head_Y_axis ? abs(head_pos_Y) : abs(head_pos_X) ;
1863  }
1864 
1865 
1866 
1867  kword = "/gate/"+head_name+"/ring/setRepeatNumber";
1868  values = CheckGATECommand(kword, line);
1869  if (values.size()>0)
1870  {
1871  if(ConvertFromString(values[0], &a_nHeads))
1872  {
1873  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
1874  return 1;
1875  }
1876  }
1877 
1878  kword = "/gate/"+head_name+"/ring/setFirstAngle";
1879  values = CheckGATECommand(kword, line);
1880  if (values.size()>0)
1881  {
1882  if(ConvertFromString(values[0], &a_head1stAngle))
1883  {
1884  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
1885  return 1;
1886  }
1887  }
1888 
1889  kword = "/gate/"+head_name+"/ring/setAngularPitch";
1890  values = CheckGATECommand(kword, line);
1891  if (values.size()>0)
1892  {
1893  if(ConvertFromString(values[0], &a_headAngPitch))
1894  {
1895  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
1896  return 1;
1897  }
1898  }
1899 
1900  kword = "/gate/"+head_name+"/moves/insert";
1901  values = CheckGATECommand(kword, line);
1902  if (values.size()>0)
1903  {
1904  if(ConvertFromString(values[0], &head_orbit_name))
1905  {
1906  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
1907  return 1;
1908  }
1909  }
1910 
1911  kword = "/gate/"+head_name+"/"+head_orbit_name+"/setSpeed";
1912  values = CheckGATECommand(kword, line);
1913  if (values.size()>0)
1914  {
1915  if(ConvertFromString(values[0], &head_rot_speed))
1916  {
1917  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
1918  return 1;
1919  }
1920  }
1921 
1922  kword = "/gate/"+head_name+"/"+head_orbit_name+"/setPoint2";
1923  values = CheckGATECommand(kword, line);
1924  if (values.size()>0)
1925  {
1926  int rot_direction;
1927  if(ConvertFromString(values[2], &rot_direction))
1928  {
1929  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
1930  return 1;
1931  }
1932 
1933  a_headRotDirection = rot_direction>0 ? GEO_ROT_CW : GEO_ROT_CCW;
1934  }
1935 
1936  // --- Crystals ---
1937  kword = is_head_Y_axis ?
1938  "/gate/"+crystal_name+"/geometry/setXLength":
1939  "/gate/"+crystal_name+"/geometry/setYLength";
1940 
1941  values = CheckGATECommand(kword, line);
1942  if (values.size()>0)
1943  {
1944  if(ConvertFromString(values[0], &a_crystalSizeTrs) )
1945  {
1946  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
1947  return 1;
1948  }
1949 
1950  }
1951 
1952  kword = "/gate/"+crystal_name+"/geometry/setZLength";
1953  values = CheckGATECommand(kword, line);
1954  if (values.size()>0)
1955  {
1956  if(ConvertFromString(values[0], &a_crystalSizeAxl) )
1957  {
1958  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
1959  return 1;
1960  }
1961  }
1962 
1963 
1964  // --- Pixels ---
1965  kword = is_head_Y_axis ?
1966  "/gate/"+pixel_name+"/cubicArray/setRepeatNumberX":
1967  "/gate/"+pixel_name+"/cubicArray/setRepeatNumberY";
1968 
1969 
1970  values = CheckGATECommand(kword, line);
1971  if (values.size()>0)
1972  {
1973  if(ConvertFromString(values[0], &a_nPixTrs) )
1974  {
1975  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
1976  return 1;
1977  }
1978  }
1979 
1980  kword = "/gate/"+pixel_name+"/cubicArray/setRepeatNumberZ";
1981  values = CheckGATECommand(kword, line);
1982  if (values.size()>0)
1983  {
1984  if(ConvertFromString(values[0], &a_nPixAxl) )
1985  {
1986  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
1987  return 1;
1988  }
1989  }
1990 
1991  kword = "/gate/application/setTimeStart";
1992  values = CheckGATECommand(kword, line);
1993  if (values.size()>0)
1994  {
1995  if(ConvertFromString( values[0] , &time_start) )
1996  {
1997  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
1998  return 1;
1999  }
2000 
2001  // Convert in ms
2002  if (values.size()>1)
2003  {
2004  if(values[1] == "s") time_start *= 1000;
2005  }
2006  else
2007  Cerr("***** dataConversionUtilities::readMacECAT()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
2008 
2009  a_start_time_ms = time_start;
2010  }
2011 
2012  kword = "/gate/application/setTimeSlice";
2013  values = CheckGATECommand(kword, line);
2014  if (values.size()>0)
2015  {
2016  if(ConvertFromString( values[0] , &time_slice) )
2017  {
2018  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
2019  return 1;
2020  }
2021 
2022  // Convert in ms
2023  if (values.size()>1)
2024  {
2025  if(values[1] == "s") time_slice *= 1000;
2026  }
2027  else
2028  Cerr("***** dataConversionUtilities::readMacECAT()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
2029 
2030  time_slice_ms = time_slice;
2031  }
2032 
2033  kword = "/gate/application/setTimeStop";
2034  values = CheckGATECommand(kword, line);
2035  if (values.size()>0)
2036  {
2037  if(ConvertFromString( values[0] , &time_stop) )
2038  {
2039  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
2040  return 1;
2041  }
2042 
2043  // Convert in ms
2044  if (values.size()>1)
2045  {
2046  if(values[1] == "s") time_stop *= 1000;
2047  }
2048  else
2049  Cerr("***** dataConversionUtilities::readMacECAT()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
2050  }
2051 
2052  kword = "/gate/application/addSlice";
2053  values = CheckGATECommand(kword, line);
2054  if (values.size()>0)
2055  {
2056  double time_slice_tmp=0;
2057 
2058  if(ConvertFromString( values[0] , &time_slice_tmp) )
2059  {
2060  Cerr("***** dataConversionUtilities::readMacECAT()->Error occurred while trying to get value from entry '"<< kword << "' in file " << path_mac_files[f] << "!");
2061  return 1;
2062  }
2063 
2064  // Convert in ms
2065  if (values.size()>1)
2066  {
2067  if(values[1] == "s") time_slice_tmp *= 1000;
2068  }
2069  else
2070  Cerr("***** dataConversionUtilities::readMacECAT()-> WARNING : can't read unit of '"<< kword << ". Assuming time in seconds");
2071 
2072  time_slices = time_slice_tmp;
2073  }
2074  }
2075 
2076  // Compute duration in ms
2077 
2078  // Time slices were provided
2079  a_duration_ms = (time_slices>0) ?
2080  (uint32_t)(time_slices-time_start) :
2081  a_duration_ms;
2082 
2083  // Time stop/start is provided
2084  a_duration_ms = (time_start>=0 && time_stop>=0) ?
2085  (uint32_t)(time_stop-time_start) :
2086  a_duration_ms;
2087 
2088 
2089  // Compute the nb of projections and total number of crystals in the system
2090  a_nProjectionsByHead = a_duration_ms / time_slice_ms;
2091  a_nProjectionsTot = a_nHeads*a_nProjectionsByHead;
2092 
2093  // Compute angular pitch if not provided in the mac files (=-1)
2094  a_headAngPitch = (a_headAngPitch<0) ?
2095  360./a_nHeads :
2096  a_headAngPitch ;
2097 
2098  a_headAngStepDeg = head_rot_speed*time_slice_ms/1000.;
2099 
2100  if(head_rot_speed<0)
2101  {
2102  Cerr("***** GetGATESystemType() -> Error couldn't find line '/gate/"+head_name+"/"+head_orbit_name+"/setSpeed' !" << endl);
2103  Cerr(" This information is mandatory to compute the projection angle step." << endl);
2104  return 1;
2105  }
2106 
2107  if(time_slice_ms<0)
2108  {
2109  Cerr("***** GetGATESystemType() -> Error couldn't find line '/gate/application/setTimeSlice' !" << endl);
2110  Cerr(" This information is mandatory to compute the projection angle step." << endl);
2111  return 1;
2112  }
2113 
2114  if(a_duration_ms == 0)
2115  {
2116  if(time_stop <0)
2117  {
2118  Cerr("***** GetGATESystemType() -> Error couldn't compute acquisition find line '/gate/application/setTimeStop' !" << endl);
2119  Cerr(" This information is mandatory to compute the acquisition duration." << endl);
2120  return 1;
2121  }
2122  if(time_start <0)
2123  {
2124  Cerr("***** GetGATESystemType() -> Error couldn't compute acquisition find line '/gate/application/setTimeStart' !" << endl);
2125  Cerr(" This information is mandatory to compute the acquisition duration." << endl);
2126  return 1;
2127  }
2128  }
2129 
2130  mac_file.close();
2131  } // end loop on .mac files
2132 
2133  if(vb >= 2)
2134  {
2135  Cout(endl);
2136  Cout("-----------------------------------------------------" << endl);
2137  Cout("ReadMacSPECT()-> Information recovered from mac file:" << endl);
2138  Cout("-----------------------------------------------------" << endl);
2139  Cout("Distance to detector: " << a_distToDetector << endl);
2140  Cout("Number of heads: " << a_nHeads << endl);
2141  Cout("Number of axial pixels: " << a_nPixAxl << endl);
2142  Cout("Number of transaxial pixels: " << a_nPixTrs << endl);
2143  Cout("Crystal axial size: " << a_crystalSizeAxl << endl);
2144  Cout("Crystal transaxial size: " << a_crystalSizeTrs << endl);
2145  Cout("Number of projections per head: " << a_nProjectionsByHead << endl);
2146  Cout("Total number of projections: " << a_nProjectionsTot << endl);
2147  Cout("Head(s) first transaxial angle: " << a_head1stAngle << endl);
2148  Cout("Head(s) angular pitch: " << a_headAngPitch << endl);
2149  Cout("Angular step between projections (deg): " << a_headAngStepDeg << endl);
2150  Cout("Rotation direction (0=CW, 1=CCW): " << a_headRotDirection << endl);
2151  Cout("Acquisition start time (ms): " << a_start_time_ms << endl);
2152  Cout("Acquisition duration (ms): " << a_duration_ms << endl);
2153  Cout("-----------------------------------------------------" << endl << endl);
2154  }
2155 
2156  return 0;
2157 }
2158 
2159 
2160 
2161 
2162 // =====================================================================
2163 // ---------------------------------------------------------------------
2164 // ---------------------------------------------------------------------
2165 // =====================================================================
2166 /*
2167  \fn ReadIntfSPECT
2168  \param a_pathIntf : path to the interfile header
2169  \param distToDetector : distance between center of rotation and detector surface
2170  \param nHeads : nb of cameras
2171  \param nPixAxl : nb of transaxial pixels
2172  \param nPixTrs : nb of axial pixels
2173  \param crystalSizeAxl : crystal axial dimension
2174  \param crystalSizeTrs : crystal transaxial dimension
2175  \param head1stAngle : head first angle
2176  \param headAngPitch : angular pitch between heads
2177  \param headRotSpeed : angle between projection
2178  \param headRotDirection : rotation direction of the head (CW or CCW)
2179  \param start_time_ms : acquisition start time converted in ms
2180  \param duration_ms : acquisition duration converted in ms
2181  \param vb : verbosity
2182  \brief Recover informations about the scanner element of an ECAT system, and acquisition duration, from a GATE macro file
2183  \return 0 if success, positive value otherwise
2184 */
2185 int ReadIntfSPECT(string a_pathIntf,
2186  float_t &a_distToDetector,
2187  uint32_t &a_nHeads,
2188  uint32_t &a_nPixAxl,
2189  uint32_t &a_nPixTrs,
2190  float_t &a_crystalSizeAxl,
2191  float_t &a_crystalSizeTrs,
2192  uint32_t &a_nProjectionsTot,
2193  uint32_t &a_nProjectionsByHead,
2194  float_t &a_head1stAngle,
2195  float_t &a_headAngPitchDeg,
2196  float_t &a_headAngStepDeg,
2197  int &a_headRotDirection,
2198  uint32_t &a_start_time_ms,
2199  uint32_t &a_duration_ms,
2200  int vb)
2201 {
2202  // Read all required information in Interfile header
2203 
2204  string key;
2205 
2206  key = "matrix size [1]";
2207  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &a_nPixTrs, 1, KEYWORD_MANDATORY))
2208  {
2209  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2210  Cerr(" Either key not found or conversion error" << endl);
2211  return 1;
2212  }
2213 
2214  key = "matrix size [2]";
2215  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &a_nPixAxl, 1, KEYWORD_MANDATORY))
2216  {
2217  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2218  Cerr(" Either key not found or conversion error" << endl);
2219  return 1;
2220  }
2221 
2222  FLTNB size_pix_trs = 1.,
2223  size_pix_axl = 1.;
2224 
2225  key = "scaling factor (mm/pixel) [1]";
2226  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &size_pix_trs, 1, KEYWORD_MANDATORY))
2227  {
2228  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2229  Cerr(" Either key not found or conversion error" << endl);
2230  return 1;
2231  }
2232 
2233  key = "scaling factor (mm/pixel) [2]";
2234  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &size_pix_axl, 1, KEYWORD_MANDATORY))
2235  {
2236  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2237  Cerr(" Either key not found or conversion error" << endl);
2238  return 1;
2239  }
2240 
2241  key = "total number of images";
2242  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &a_nProjectionsTot, 1, KEYWORD_MANDATORY))
2243  {
2244  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2245  Cerr(" Either key not found or conversion error" << endl);
2246  return 1;
2247  }
2248 
2249  key = "number of projections";
2250  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &a_nProjectionsByHead, 1, KEYWORD_MANDATORY))
2251  {
2252  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2253  Cerr(" Either key not found or conversion error" << endl);
2254  return 1;
2255  }
2256 
2257  key = "number of detector heads";
2258  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &a_nHeads, 1, KEYWORD_MANDATORY))
2259  {
2260  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2261  Cerr(" Either key not found or conversion error" << endl);
2262  return 1;
2263  }
2264 
2265  key = "study duration (sec)";
2266  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &a_duration_ms, 1, KEYWORD_MANDATORY))
2267  {
2268  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2269  Cerr(" Either key not found or conversion error" << endl);
2270  return 1;
2271  }
2272  // convert duration in ms
2273  a_duration_ms *= 1000;
2274 
2275 
2276  FLTNB size_crystal_X =0.,
2277  size_crystal_Y =0.;
2278 
2279  key = "crystal x dimension (cm)";
2280  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &size_crystal_X, 1, KEYWORD_OPTIONAL) == 1)
2281  {
2282  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2283  Cerr(" Either key not found or conversion error" << endl);
2284  return 1;
2285  }
2286 
2287  key = "crystal y dimension (cm)";
2288  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &size_crystal_Y, 1, KEYWORD_OPTIONAL) == 1)
2289  {
2290  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2291  Cerr(" Either key not found or conversion error" << endl);
2292  return 1;
2293  }
2294 
2295  key = "crystal z dimension (cm)";
2296  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &a_crystalSizeAxl, 1, KEYWORD_OPTIONAL) == 1)
2297  {
2298  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2299  Cerr(" Either key not found or conversion error" << endl);
2300  return 1;
2301  }
2302 
2303  // Just pick the larger value to get the transaxial crystal size
2304  // convert in mm (values in cm in Interfile)
2305  if(size_crystal_X>0 && size_crystal_Y>0)
2306  a_crystalSizeTrs = (size_crystal_X>size_crystal_Y) ? size_crystal_X*10. : size_crystal_Y*10.;
2307  a_crystalSizeAxl = (a_crystalSizeAxl>0) ? a_crystalSizeAxl*10. : a_crystalSizeAxl ;
2308 
2309 
2310  FLTNB head_pos_X =-1.,
2311  head_pos_Y =-1.,
2312  head_pos_Z =-1.;
2313 
2314  key = "head x translation (cm)";
2315  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &head_pos_X, 1, KEYWORD_OPTIONAL) == 1)
2316  {
2317  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2318  Cerr(" Either key not found or conversion error" << endl);
2319  return 1;
2320  }
2321 
2322  key = "head y translation (cm)";
2323  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &head_pos_Y, 1, KEYWORD_OPTIONAL) == 1)
2324  {
2325  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2326  Cerr(" Either key not found or conversion error" << endl);
2327  return 1;
2328  }
2329 
2330  key = "head z translation (cm)";
2331  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &head_pos_Z, 1, KEYWORD_OPTIONAL) == 1)
2332  {
2333  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2334  Cerr(" Either key not found or conversion error" << endl);
2335  return 1;
2336  }
2337 
2338  // Pick the largest value to initialize distance betweeen COR and detector, converted in mm (cm by default)
2339  if(head_pos_X > head_pos_Y)
2340  a_distToDetector = head_pos_X > head_pos_Z ? head_pos_X*10 : head_pos_Z*10;
2341  else
2342  a_distToDetector = head_pos_Y > head_pos_Z ? head_pos_Y*10 : head_pos_Z*10;
2343 
2344 
2345  key = "direction of rotation";
2346  string rot_dir;
2347  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &rot_dir, 1, KEYWORD_MANDATORY))
2348  {
2349  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2350  Cerr(" Either key not found or conversion error" << endl);
2351  return 1;
2352  }
2353 
2354  a_headRotDirection = (rot_dir == "CCW") ? GEO_ROT_CCW : GEO_ROT_CW ;
2355 
2356  // Could have several keys with this name (each for each head)
2357  // It will recover the value corresponding to the first match
2358  key = "start angle";
2359  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &a_head1stAngle, 1, KEYWORD_MANDATORY))
2360  {
2361  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2362  Cerr(" Either key not found or conversion error" << endl);
2363  return 1;
2364  }
2365 
2366  key = "extent of rotation";
2367  uint32_t extent_rotation =360;
2368  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &extent_rotation, 1, KEYWORD_MANDATORY))
2369  {
2370  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2371  Cerr(" Either key not found or conversion error" << endl);
2372  return 1;
2373  }
2374 
2375  a_headAngStepDeg = (FLTNB)extent_rotation / a_nProjectionsByHead;
2376 
2377  float_t first_angle = 0;
2378  float_t second_angle = extent_rotation;
2379 
2380  key = "start angle";
2381  if(IntfKeyGetValueFromFile(a_pathIntf.c_str(), key.c_str(), &first_angle, 1, KEYWORD_MANDATORY))
2382  {
2383  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2384  Cerr(" Either key not found or conversion error" << endl);
2385  return 1;
2386  }
2387 
2388  key = "start angle";
2389  if(IntfKeyGetRecurringValueFromFile(a_pathIntf.c_str(), key.c_str(), &second_angle, 1, KEYWORD_MANDATORY, 1))
2390  {
2391  Cerr("***** castor-GATERootToCastor :: Error when trying to read key: '"<< key << "' in interfile : " << a_pathIntf << "!" << endl);
2392  Cerr(" Either key not found or conversion error" << endl);
2393  return 1;
2394  }
2395 
2396  a_headAngPitchDeg = second_angle - first_angle;
2397 
2398  return 0;
2399 }
2400 
2401 
2402 
2403 
2404 // =====================================================================
2405 // ---------------------------------------------------------------------
2406 // ---------------------------------------------------------------------
2407 // =====================================================================
2408 /*
2409  \fn CreateGeomWithCylindrical()
2410  \param a_pathMac : string containing the path to a GATE macro file
2411  \param a_pathGeom : string containing the path to a CASToR output geom file
2412  \brief Read a GATE macro file containing the description of a cylindricalPET system, and convert it to a geom file
2413  \return 0 if success, positive value otherwise
2414 */
2415 int CreateGeomWithCylindrical(string a_pathMac, string a_pathGeom)
2416 {
2417  /* Declaring variables */
2418 
2419  // Scanner
2420  string modality;
2421  string scanner_name = GetFileFromPath(a_pathGeom.substr(0,a_pathGeom.find(".geom")));
2422  double scanner_radius = 0.;
2423  uint32_t number_of_elements = 0;
2424  string description = "PET system extracted from GATE macro: " + a_pathMac;
2425 
2426  // Rsectors
2427  string rsector_name = "";
2428  uint32_t number_of_rsectors_ang = 1; // repeater on a ring
2429  uint32_t number_of_rsectors_axl = 1; // linear repeater
2430  double rsector_step_axl = 0.;
2431  double rsector_gap_axl = 0.;
2432  double rsector_first_angle = 0.;
2433  double rsector_angular_span = 360.;
2434  double rsector_pos_X = 0.;
2435  double rsector_pos_Y = 0.;
2436  uint32_t rsector_nb_zshifts = 0;
2437  vector <double> vec_rsector_Z_Shift;
2438  bool is_rsector_Y_axis = false;
2439  double rsector_size_trs;
2440  double rsector_size_axl;
2441 
2442  // Modules
2443  string module_name = "";
2444  uint32_t number_of_modules_trs = 1;
2445  uint32_t number_of_modules_axl = 1;
2446  double module_step_trs = 0.;
2447  double module_step_axl = 0.;
2448  double module_gap_trs = 0.;
2449  double module_gap_axl = 0.;
2450  double module_size_trs;
2451  double module_size_axl;
2452 
2453  // Submodules
2454  string submodule_name = "";
2455  uint32_t number_of_submodules_trs = 1;
2456  uint32_t number_of_submodules_axl = 1;
2457  double submodule_step_trs = 0.;
2458  double submodule_step_axl = 0.;
2459  double submodule_gap_trs = 0.;
2460  double submodule_gap_axl = 0.;
2461  double submodule_size_trs;
2462  double submodule_size_axl;
2463 
2464  // Crystals
2465  string crystal_name = "";
2466  uint32_t number_of_crystals_trs = 1;
2467  uint32_t number_of_crystals_axl = 1;
2468  double crystal_step_trs = 0.;
2469  double crystal_step_axl = 0.;
2470  double crystal_gap_trs = 0.;
2471  double crystal_gap_axl = 0.;
2472  double crystal_size_depth = 0.;
2473  double crystal_size_trs = 0.;
2474  double crystal_size_axl = 0.;
2475 
2476 
2477  // Layers
2478  int number_of_layers = 0;
2479  string n_layers = "1"; //default value for number_of_layers
2480  vector <uint32_t> number_of_lyr_elts_trs;
2481  vector <uint32_t> number_of_lyr_elts_axl;
2482 
2483  vector <string> layers_names;
2484  vector <vector <double> > layers_positions;
2485  vector <double> layers_size_depth;
2486  vector <double> layers_size_trs;
2487  vector <double> layers_size_axl;
2488 
2489  vector <double> layers_step_trs;
2490  vector <double> layers_step_axl;
2491 
2492  // Optional
2493  uint32_t voxels_number_trs;
2494  uint32_t voxels_number_axl;
2495  double fov_trs,
2496  fov_axl;
2497  double mean_depth_of_interaction = -1.;
2498  double min_angle_diff = 0.;
2499 
2500 
2501 
2502  // If we have multiple layers, we need to enter multiple values in the .geom.
2503  vector <string> vec_scanner_radius;
2504 
2505  // Rsector vectors
2506  vector <string> vec_number_of_rsectors_ang;
2507  vector <string> vec_number_of_rsectors_axl;
2508  vector <string> vec_rsector_gap_trs;
2509  vector <string> vec_rsector_gap_axl;
2510  vector <string> vec_rsector_first_angle;
2511 
2512 
2513  // Module vectors
2514  vector <string> vec_number_of_modules_trs;
2515  vector <string> vec_number_of_modules_axl;
2516  vector <string> vec_module_gap_trs;
2517  vector <string> vec_module_gap_axl;
2518 
2519  // Submodule vectors
2520  vector <string> vec_number_of_submodules_trs;
2521  vector <string> vec_number_of_submodules_axl;
2522  vector <string> vec_submodule_gap_trs;
2523  vector <string> vec_submodule_gap_axl;
2524 
2525  // Crystal vectors
2526  vector <string> vec_number_of_crystals_trs;
2527  vector <string> vec_number_of_crystals_axl;
2528  vector <string> vec_crystal_gap_trs;
2529  vector <string> vec_crystal_gap_axl;
2530 
2531  // Optionnal
2532  vector <string> vec_mean_depth_of_interaction;
2533  vector <string> vec_min_angle_diff;
2534 
2535  vector<string> path_mac_files;
2536  path_mac_files.push_back(a_pathMac);
2537 
2538  // Recover path to all macro files from the main mac file
2539  if(GetGATEMacFiles(a_pathMac , path_mac_files))
2540  {
2541  Cerr("***** GetGATESystemType() ->Error occurred when trying to recover paths to GATE macro files !" << endl);
2542  return 1;
2543  }
2544 
2545  // Recover aliases of the different parts of the architecture
2546  if(GetGATEAliasesCylindrical(path_mac_files, rsector_name, module_name, submodule_name, crystal_name, layers_names, 2) )
2547  {
2548  Cerr("***** GetGATESystemType() ->Error occurred when trying to recover aliases for the elements of the cylindricalPET !" << endl);
2549  return 1;
2550  }
2551 
2552  // Recover nb of detected layers
2553  n_layers = layers_names.size();
2554 
2555 
2556  // Loop to recover all other informations
2557  for(uint16_t f=0 ; f<path_mac_files.size() ; f++)
2558  {
2559  ifstream systemMac(path_mac_files[f].c_str(), ios::in);
2560 
2561  // Reading the .mac file line by line and update the variables if a matching adress is found
2562  string line;
2563  while(getline(systemMac, line))
2564  {
2565  vector <string> values;
2566 
2567  // Scanner
2568  modality = "PET";
2569 
2570  string entry = "";
2571 
2572  entry = "/gate/"+rsector_name+"/placement/setTranslation";
2573  values = CheckGATECommand(entry, line);
2574 
2575  // Assumes that first rsector located on the X or Y axis
2576  if (values.size()>0)
2577  {
2578  if(ConvertFromString(values[0], &rsector_pos_X) ||
2579  ConvertFromString(values[1], &rsector_pos_Y) )
2580  {
2581  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2582  return 1;
2583  }
2584 
2585  // Check first rsector cartesian coordinates is 0 on X or Y axis
2586  // Throw error otherwise
2587  if(rsector_pos_X!=0 && rsector_pos_Y !=0)
2588  {
2589  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2590  Cerr(" Rsector cartesian coordinates on either the X or Y axis expected to be equal to 0 "<< endl);
2591  return 1;
2592  }
2593 
2594  // Get the axis on which the first rsector is positionned
2595  if(rsector_pos_Y!=0) is_rsector_Y_axis = true;
2596 
2597  // Adjust scanner radius to the center position of the rsector
2598  scanner_radius += is_rsector_Y_axis ? abs(rsector_pos_Y) : abs(rsector_pos_X) ;
2599  }
2600 
2601  /*
2602  // Adjust scanner radius (from center to rsector surface) according to block size
2603  entry = is_rsector_Y_axis ?
2604  "/gate/"+rsector_name+"/geometry/setYLength" :
2605  "/gate/"+rsector_name+"/geometry/setXLength";
2606 
2607  values = CheckGATECommand(entry, line);
2608  if (values.size()>0)
2609  {
2610  double rsector_size_depth = 0.;
2611  if(ConvertFromString(values[0], &rsector_size_depth) )
2612  {
2613  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2614  return 1;
2615  }
2616 
2617  scanner_radius -= rsector_size_depth/2;
2618  }
2619  */
2620 
2621 
2622  // rsector transaxial size
2623  entry = is_rsector_Y_axis ?
2624  "/gate/"+rsector_name+"/geometry/setXLength":
2625  "/gate/"+rsector_name+"/geometry/setYLength";
2626 
2627  values = CheckGATECommand(entry, line);
2628  if (values.size()>0)
2629  {
2630  if(ConvertFromString(values[0], &rsector_size_trs) )
2631  {
2632  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2633  return 1;
2634  }
2635  }
2636 
2637 
2638 
2639  // --- Rsectors ---
2640 
2641  entry = "/gate/"+rsector_name+"/ring/setModuloNumber";
2642  values = CheckGATECommand(entry, line);
2643  if (values.size()>0)
2644  {
2645  if(ConvertFromString(values[0], &rsector_nb_zshifts) )
2646  {
2647  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2648  return 1;
2649  }
2650  }
2651 
2652  entry = "/gate/"+rsector_name+"/ring/setRepeatNumber";
2653  values = CheckGATECommand(entry, line);
2654  if (values.size()>0)
2655  {
2656  if(ConvertFromString(values[0], &number_of_rsectors_ang) )
2657  {
2658  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2659  return 1;
2660  }
2661  }
2662 
2663 
2664 
2665  // Check if (axial) linear repeaters for rsectors
2666  entry = "/gate/"+rsector_name+"/linear/setRepeatNumber";
2667  values = CheckGATECommand(entry, line);
2668  if (values.size()>0)
2669  {
2670  if(ConvertFromString(values[0], &number_of_rsectors_axl) )
2671  {
2672  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2673  return 1;
2674  }
2675  }
2676 
2677 
2678 
2679 
2680  entry = "/gate/"+rsector_name+"/linear/setRepeatVector";
2681  values = CheckGATECommand(entry, line);
2682  if (values.size()>0)
2683  {
2684  if(ConvertFromString(values[2], &rsector_step_axl) )
2685  {
2686  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2687  return 1;
2688  }
2689  }
2690 
2691 
2692  // Check if cubic array repeaters for rsectors
2693  entry = is_rsector_Y_axis ?
2694  "/gate/"+rsector_name+"/cubicArray/setRepeatNumberX":
2695  "/gate/"+rsector_name+"/cubicArray/setRepeatNumberY";
2696 
2697  values = CheckGATECommand(entry, line);
2698  if (values.size()>0)
2699  {
2700  uint32_t number_of_rsectors_trs;
2701 
2702  if(ConvertFromString(values[0], &number_of_rsectors_trs) )
2703  {
2704  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2705  return 1;
2706  }
2707 
2708  // Check if more than one transaxial rsector is present.
2709  // Not currently implemented as the geometry model can vary according to whether a ring repeater is inserted before or after the cubicarray repeater
2710  // Throw error in this situation
2711  if(number_of_rsectors_trs>1)
2712  {
2713  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Error while trying to parse line: " << line<< endl);
2714  Cerr(" The GATE system contains more than one 'transaxial' rsector " << endl);
2715  Cerr(" The current implementation does not support such cylindricalPET model" << endl);
2716  Cerr(" Manual implementation of the system is required (ex: model the transaxial rsectors as modules)" << endl);
2717  return 1;
2718  }
2719  }
2720 
2721 
2722  entry = "/gate/"+rsector_name+"/cubicArray/setRepeatNumberZ";
2723  values = CheckGATECommand(entry, line);
2724 
2725  if (values.size()>0)
2726  {
2727  if(ConvertFromString(values[0], &number_of_rsectors_axl) )
2728  {
2729  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2730  return 1;
2731  }
2732  }
2733 
2734 
2735  entry = "/gate/"+rsector_name+"/cubicArray/setRepeatVector";
2736  values = CheckGATECommand(entry, line);
2737  if (values.size()>0)
2738  {
2739  if(ConvertFromString(values[2], &rsector_step_axl))
2740  {
2741  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2742  return 1;
2743  }
2744  }
2745 
2746 
2747 
2748  // axial FOV computed from rsector z-length
2749  entry = "/gate/"+rsector_name+"/geometry/setZLength";
2750  values = CheckGATECommand(entry, line);
2751  if (values.size()>0)
2752  {
2753  if(ConvertFromString(values[0], &rsector_size_axl) )
2754  {
2755  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2756  return 1;
2757  }
2758 
2759  }
2760 
2761 
2762  entry = "/gate/"+rsector_name+"/ring/setFirstAngle";
2763  values = CheckGATECommand(entry, line);
2764  if (values.size()>0)
2765  {
2766  if(ConvertFromString(values[0], &rsector_first_angle) )
2767  {
2768  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2769  return 1;
2770  }
2771  }
2772 
2773  entry = "/gate/"+rsector_name+"/ring/setAngularSpan";
2774  values = CheckGATECommand(entry, line);
2775  if (values.size()>0)
2776  {
2777  if(ConvertFromString(values[0], &rsector_angular_span) )
2778  {
2779  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2780  return 1;
2781  }
2782  }
2783 
2784  // Up to 8 zshifts in Gate
2785  for (int i=1; i <8; i++)
2786  {
2787  entry = "/gate/"+rsector_name+"/ring/setZShift"+toString(i);
2788  values = CheckGATECommand(entry, line);
2789  if (values.size()>0)
2790  {
2791  double zshift = 0.;
2792  if(ConvertFromString(values[0], &zshift) )
2793  {
2794  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2795  return 1;
2796  }
2797 
2798  vec_rsector_Z_Shift.push_back(zshift);
2799  }
2800  }
2801 
2802 
2803 
2804  // --- Modules ---
2805 
2806  // Check for any translation and adjust scanner radius position accordingly
2807  entry = "/gate/"+module_name+"/placement/setTranslation";
2808  values = CheckGATECommand(entry, line);
2809 
2810  FLTNB module_pos_X =0.,
2811  module_pos_Y =0.;
2812 
2813  // Assumes that module located on the X or Y axis
2814  if (values.size()>0)
2815  {
2816  if(ConvertFromString(values[0], &module_pos_X) ||
2817  ConvertFromString(values[1], &module_pos_Y) )
2818  {
2819  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2820  return 1;
2821  }
2822 
2823  // Check first module cartesian coordinates is 0 on X or Y axis
2824  // Throw error otherwise
2825  if(module_pos_X!=0 && module_pos_Y !=0)
2826  {
2827  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2828  Cerr(" Module cartesian coordinates on either the X or Y axis expected to be equal to 0 "<< endl);
2829  return 1;
2830  }
2831 
2832  // Adjust scanner radius to the center position of module
2833  scanner_radius += is_rsector_Y_axis ? abs(module_pos_Y) : abs(module_pos_X) ;
2834  }
2835 
2836 
2837  entry = is_rsector_Y_axis ?
2838  "/gate/"+module_name+"/cubicArray/setRepeatNumberX":
2839  "/gate/"+module_name+"/cubicArray/setRepeatNumberY";
2840 
2841  values = CheckGATECommand(entry, line);
2842  if (values.size()>0)
2843  {
2844  if(ConvertFromString(values[0], &number_of_modules_trs) )
2845  {
2846  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2847  return 1;
2848  }
2849  }
2850 
2851 
2852 
2853 
2854  entry = "/gate/"+module_name+"/cubicArray/setRepeatNumberZ";
2855  values = CheckGATECommand(entry, line);
2856 
2857  if (values.size()>0)
2858  {
2859  if(ConvertFromString(values[0], &number_of_modules_axl) )
2860  {
2861  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2862  return 1;
2863  }
2864  }
2865 
2866 
2867  entry = is_rsector_Y_axis ?
2868  "/gate/"+module_name+"/geometry/setXLength":
2869  "/gate/"+module_name+"/geometry/setYLength";
2870 
2871  values = CheckGATECommand(entry, line);
2872  if (values.size()>0)
2873  {
2874  if(ConvertFromString(values[0], &module_size_trs) )
2875  {
2876  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2877  return 1;
2878  }
2879  }
2880 
2881 
2882  entry = "/gate/"+module_name+"/geometry/setZLength";
2883  values = CheckGATECommand(entry, line);
2884  if (values.size()>0)
2885  {
2886  if(ConvertFromString(values[0], &module_size_axl) )
2887  {
2888  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2889  return 1;
2890  }
2891  }
2892 
2893  entry = "/gate/"+module_name+"/cubicArray/setRepeatVector";
2894  values = CheckGATECommand(entry, line);
2895  if (values.size()>0)
2896  {
2897  string trs_step = is_rsector_Y_axis ?
2898  values[0] :
2899  values[1] ;
2900 
2901  if(ConvertFromString(trs_step, &module_step_trs) ||
2902  ConvertFromString(values[2], &module_step_axl))
2903  {
2904  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2905  return 1;
2906  }
2907  }
2908 
2909 
2910 
2911 
2912  // linear repeaters instead of box
2913  entry = "/gate/"+module_name+"/linear/setRepeatNumber";
2914  values = CheckGATECommand(entry, line);
2915 
2916  if (values.size()>0)
2917  {
2918  if(ConvertFromString(values[0] , &number_of_modules_axl) )
2919  {
2920  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2921  return 1;
2922  }
2923  }
2924 
2925  entry = "/gate/"+module_name+"/linear/setRepeatVector";
2926  values = CheckGATECommand(entry, line);
2927  if (values.size()>0)
2928  {
2929  if(ConvertFromString(values[2], &module_step_axl))
2930  {
2931  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2932  return 1;
2933  }
2934  }
2935 
2936 
2937  // --- Submodules ---
2938 
2939  // Check for any translation and adjust scanner radius position accordingly
2940  entry = "/gate/"+submodule_name+"/placement/setTranslation";
2941  values = CheckGATECommand(entry, line);
2942 
2943  FLTNB submodule_pos_X =0.,
2944  submodule_pos_Y =0.;
2945 
2946  // Assumes that submodule located on the X or Y axis
2947  if (values.size()>0)
2948  {
2949  if(ConvertFromString(values[0], &submodule_pos_X) ||
2950  ConvertFromString(values[1], &submodule_pos_Y) )
2951  {
2952  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2953  return 1;
2954  }
2955 
2956  // Check first module cartesian coordinates is 0 on X or Y axis
2957  // Throw error otherwise
2958  if(submodule_pos_X!=0 && submodule_pos_Y !=0)
2959  {
2960  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2961  Cerr(" Submodule cartesian coordinates on either the X or Y axis expected to be equal to 0 "<< endl);
2962  return 1;
2963  }
2964 
2965  // Adjust scanner radius to the center position of submodule
2966  scanner_radius += is_rsector_Y_axis ? abs(submodule_pos_Y) : abs(submodule_pos_X) ;
2967  }
2968 
2969 
2970 
2971  entry = is_rsector_Y_axis ?
2972  "/gate/"+submodule_name+"/cubicArray/setRepeatNumberX":
2973  "/gate/"+submodule_name+"/cubicArray/setRepeatNumberY";
2974 
2975 
2976  values = CheckGATECommand(entry, line);
2977  if (values.size()>0)
2978  {
2979  if(ConvertFromString(values[0], &number_of_submodules_trs) )
2980  {
2981  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2982  return 1;
2983  }
2984  }
2985 
2986  entry = "/gate/"+submodule_name+"/cubicArray/setRepeatNumberZ";
2987  values = CheckGATECommand(entry, line);
2988  if (values.size()>0)
2989  {
2990  if(ConvertFromString(values[0], &number_of_submodules_axl) )
2991  {
2992  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
2993  return 1;
2994  }
2995  }
2996 
2997 
2998 
2999 
3000  entry = is_rsector_Y_axis ?
3001  "/gate/"+submodule_name+"/geometry/setXLength":
3002  "/gate/"+submodule_name+"/geometry/setYLength";
3003 
3004  values = CheckGATECommand(entry, line);
3005  if (values.size()>0)
3006  {
3007  if(ConvertFromString(values[0], &submodule_size_trs) )
3008  {
3009  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3010  return 1;
3011  }
3012  }
3013 
3014  entry = "/gate/"+submodule_name+"/geometry/setZLength";
3015  values = CheckGATECommand(entry, line);
3016  if (values.size()>0)
3017  {
3018  if(ConvertFromString(values[0], &submodule_size_axl) )
3019  {
3020  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3021  return 1;
3022  }
3023  }
3024 
3025  entry = "/gate/"+submodule_name+"/cubicArray/setRepeatVector";
3026  values = CheckGATECommand(entry, line);
3027  if (values.size()>0)
3028  {
3029  string trs_step = is_rsector_Y_axis ?
3030  values[0] :
3031  values[1] ;
3032 
3033  if(ConvertFromString(trs_step, &submodule_step_trs) ||
3034  ConvertFromString(values[2], &submodule_step_axl) )
3035  {
3036  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3037  return 1;
3038  }
3039  }
3040 
3041 
3042 
3043  // linear repeaters instead of box
3044  entry = "/gate/"+submodule_name+"/linear/setRepeatNumber";
3045  values = CheckGATECommand(entry, line);
3046 
3047  if (values.size()>0)
3048  {
3049  if(ConvertFromString( values[0] , &number_of_submodules_axl) )
3050  {
3051  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3052  return 1;
3053  }
3054  }
3055 
3056 
3057  entry = "/gate/"+submodule_name+"/linear/setRepeatVector";
3058  values = CheckGATECommand(entry, line);
3059  if (values.size()>0)
3060  {
3061  if(ConvertFromString(values[2], &submodule_step_axl))
3062  {
3063  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3064  return 1;
3065  }
3066  }
3067 
3068 
3069 
3070 
3071  // --- Crystals ---
3072 
3073  // Check for any translation and adjust scanner radius position accordingly
3074  entry = "/gate/"+crystal_name+"/placement/setTranslation";
3075  values = CheckGATECommand(entry, line);
3076 
3077  FLTNB crystal_pos_X =0.,
3078  crystal_pos_Y =0.;
3079 
3080  // Assumes that submodule located on the X or Y axis
3081  if (values.size()>0)
3082  {
3083  if(ConvertFromString(values[0], &crystal_pos_X) ||
3084  ConvertFromString(values[1], &crystal_pos_Y) )
3085  {
3086  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3087  return 1;
3088  }
3089 
3090  // Check first module cartesian coordinates is 0 on X or Y axis
3091  // Throw error otherwise
3092  if(crystal_pos_X!=0 && crystal_pos_Y !=0)
3093  {
3094  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3095  Cerr(" Crystal cartesian coordinates on either the X or Y axis expected to be equal to 0 "<< endl);
3096  return 1;
3097  }
3098 
3099  // Adjust scanner radius to the center position of crystal
3100  scanner_radius += is_rsector_Y_axis ? abs(crystal_pos_Y) : abs(crystal_pos_X) ;
3101  }
3102 
3103 
3104 
3105  entry = is_rsector_Y_axis ?
3106  "/gate/"+crystal_name+"/cubicArray/setRepeatNumberX":
3107  "/gate/"+crystal_name+"/cubicArray/setRepeatNumberY";
3108 
3109 
3110  values = CheckGATECommand(entry, line);
3111  if (values.size()>0)
3112  {
3113  if(ConvertFromString(values[0], &number_of_crystals_trs) )
3114  {
3115  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3116  return 1;
3117  }
3118  }
3119 
3120  entry = "/gate/"+crystal_name+"/cubicArray/setRepeatNumberZ";
3121  values = CheckGATECommand(entry, line);
3122  if (values.size()>0)
3123  {
3124  if(ConvertFromString(values[0], &number_of_crystals_axl) )
3125  {
3126  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3127  return 1;
3128  }
3129  }
3130 
3131 
3132  entry = "/gate/"+crystal_name+"/geometry/setXLength";
3133  values = CheckGATECommand(entry, line);
3134  if (values.size()>0)
3135  {
3136  double x_length;
3137  if(ConvertFromString(values[0], &x_length) )
3138  {
3139  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3140  return 1;
3141  }
3142 
3143  if (is_rsector_Y_axis)
3144  crystal_size_trs = x_length;
3145  else
3146  crystal_size_depth = x_length;
3147  }
3148 
3149  entry = "/gate/"+crystal_name+"/geometry/setYLength";
3150  values = CheckGATECommand(entry, line);
3151  if (values.size()>0)
3152  {
3153  double y_length;
3154  if(ConvertFromString(values[0], &y_length) )
3155  {
3156  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3157  return 1;
3158  }
3159 
3160  if (is_rsector_Y_axis)
3161  crystal_size_depth = y_length;
3162  else
3163  crystal_size_trs = y_length;
3164 
3165  }
3166 
3167  entry = "/gate/"+crystal_name+"/geometry/setZLength";
3168  values = CheckGATECommand(entry, line);
3169  if (values.size()>0)
3170  {
3171  if(ConvertFromString(values[0], &crystal_size_axl) )
3172  {
3173  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3174  return 1;
3175  }
3176  }
3177 
3178  entry = "/gate/"+crystal_name+"/cubicArray/setRepeatVector";
3179  values = CheckGATECommand(entry, line);
3180  if (values.size()>0)
3181  {
3182  string trs_step = is_rsector_Y_axis ?
3183  values[0] :
3184  values[1] ;
3185 
3186  if(ConvertFromString(trs_step, &crystal_step_trs) ||
3187  ConvertFromString(values[2], &crystal_step_axl) )
3188  {
3189  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3190  return 1;
3191  }
3192  }
3193 
3194  // linear repeaters instead of box
3195  entry = "/gate/"+crystal_name+"/linear/setRepeatNumber";
3196  values = CheckGATECommand(entry, line);
3197 
3198  if (values.size()>0)
3199  {
3200  if(ConvertFromString( values[0] , &number_of_crystals_axl) )
3201  {
3202  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3203  return 1;
3204  }
3205  }
3206 
3207  entry = "/gate/"+crystal_name+"/linear/setRepeatVector";
3208  values = CheckGATECommand(entry, line);
3209  if (values.size()>0)
3210  {
3211  if(ConvertFromString(values[2], &crystal_step_axl))
3212  {
3213  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3214  return 1;
3215  }
3216  }
3217 
3218 
3219  // --- Layers ---
3220  entry = "/gate/"+crystal_name+"/daughters/name";
3221  values = CheckGATECommand(entry, line);
3222  if (values.size()>0)
3223  {
3224  layers_names.push_back(values[0]);
3225  number_of_layers++;
3226  }
3227 
3228  // loop on the layers to get their specific information as they are integrated in layers_names.
3229  for (int i=0; i < number_of_layers; i++)
3230  {
3231  // assumes that first block is positionned on the x-axis
3232  entry = "/gate/"+layers_names[i]+"/placement/setTranslation";
3233  values = CheckGATECommand(entry, line);
3234 
3235  if (values.size()>0)
3236  {
3237  vector<double> layer_pos;
3238  for(int d=0 ; d<3 ; d++)
3239  {
3240  double pos;
3241  if(ConvertFromString(values[d], &pos) )
3242  {
3243  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3244  return 1;
3245  }
3246  layer_pos.push_back(pos);
3247  }
3248 
3249  if(is_rsector_Y_axis)
3250  {
3251  double pos = layer_pos[1];
3252  layer_pos[1] = layer_pos[0];
3253  layer_pos[0] = pos;
3254  }
3255 
3256  layers_positions.push_back(layer_pos);
3257  }
3258 
3259 
3260  // assumes that first block is positionned on the x-axis
3261  entry = "/gate/"+layers_names[i]+"/geometry/setXLength";
3262  values = CheckGATECommand(entry, line);
3263 
3264  if (values.size()>0)
3265  {
3266  double xlength;
3267  if(ConvertFromString(values[0], &xlength) )
3268  {
3269  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3270  return 1;
3271  }
3272 
3273  if (is_rsector_Y_axis)
3274  layers_size_trs.push_back(xlength);
3275  else
3276  layers_size_depth.push_back(xlength);
3277  }
3278 
3279  // assumes that first block is positionned on the x-axis
3280  entry = "/gate/"+layers_names[i]+"/geometry/setYLength";
3281  values = CheckGATECommand(entry, line);
3282  if (values.size()>0)
3283  {
3284  double ylength;
3285  if(ConvertFromString(values[0], &ylength) )
3286  {
3287  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3288  return 1;
3289  }
3290  if (is_rsector_Y_axis)
3291  layers_size_depth.push_back(ylength);
3292  else
3293  layers_size_trs.push_back(ylength);
3294  }
3295 
3296  entry = "/gate/"+layers_names[i]+"/geometry/setZLength";
3297  values = CheckGATECommand(entry, line);
3298  if (values.size()>0)
3299  {
3300  double zlength;
3301  if(ConvertFromString(values[0], &zlength) )
3302  {
3303  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3304  return 1;
3305  }
3306  layers_size_axl.push_back(zlength);
3307  }
3308 
3309  // Check if a repeater if applied to the layers elements
3310  // In this case, the total number of crystals for a layer[i]
3311  // will be crystal elts*layers elts[i]
3312  entry = is_rsector_Y_axis ?
3313  "/gate/"+layers_names[i]+"/cubicArray/setRepeatNumberX":
3314  "/gate/"+layers_names[i]+"/cubicArray/setRepeatNumberY";
3315 
3316  values = CheckGATECommand(entry, line);
3317  if (values.size()>0)
3318  {
3319  uint32_t step_trs;
3320  if(ConvertFromString(values[0], &step_trs))
3321  {
3322  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3323  return 1;
3324  }
3325  number_of_lyr_elts_trs.push_back(step_trs);
3326  }
3327 
3328  entry = "/gate/"+layers_names[i]+"/cubicArray/setRepeatNumberZ";
3329  values = CheckGATECommand(entry, line);
3330  if (values.size()>0)
3331  {
3332  uint32_t step_axl;
3333  if(ConvertFromString(values[0], &step_axl))
3334  {
3335  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3336  return 1;
3337  }
3338  number_of_lyr_elts_axl.push_back(step_axl);
3339  }
3340 
3341  entry = "/gate/"+layers_names[i]+"/cubicArray/setRepeatVector";
3342  values = CheckGATECommand(entry, line);
3343  if (values.size()>0)
3344  {
3345  string trs_step = is_rsector_Y_axis ?
3346  values[0] :
3347  values[1] ;
3348 
3349  double step_trs, step_axl;
3350  if(ConvertFromString(trs_step, &step_trs) ||
3351  ConvertFromString(values[2], &step_axl) )
3352  {
3353  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3354  return 1;
3355  }
3356 
3357 
3358  layers_step_trs.push_back(step_trs);
3359  layers_step_axl.push_back(step_axl);
3360  }
3361 
3362 
3363  // linear repeaters instead of box
3364  entry = "/gate/"+layers_names[i]+"/linear/setRepeatNumber";
3365  values = CheckGATECommand(entry, line);
3366 
3367  if (values.size()>0)
3368  {
3369  uint32_t step_axl;
3370  if(ConvertFromString( values[0] , &step_axl) )
3371  {
3372  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3373  return 1;
3374  }
3375  number_of_lyr_elts_axl.push_back(step_axl);
3376  }
3377 
3378 
3379  entry = "/gate/"+layers_names[i]+"/linear/setRepeatVector";
3380  values = CheckGATECommand(entry, line);
3381  if (values.size()>0)
3382  {
3383  double step_axl;
3384  if(ConvertFromString(values[2], &step_axl))
3385  {
3386  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3387  return 1;
3388  }
3389 
3390  layers_step_axl.push_back(step_axl);
3391  }
3392  }
3393 
3394 
3395  entry = "/gate/digitizer/Coincidences/minSectorDifference";
3396  values = CheckGATECommand(entry, line);
3397  if (values.size()>0)
3398  {
3399  FLTNB min_sector_diff= 0.;
3400 
3401  if(ConvertFromString(values[0], &min_sector_diff))
3402  {
3403  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3404  return 1;
3405  }
3406 
3407  min_angle_diff = 360./number_of_rsectors_ang*min_sector_diff;
3408  }
3409 
3410  }
3411  systemMac.close();
3412 
3413  }
3414 
3415  // Compute total number of crystal elements
3416  if(number_of_layers == 0)
3417  {
3418  number_of_elements = number_of_rsectors_ang
3419  * number_of_rsectors_axl
3420  * number_of_modules_trs
3421  * number_of_modules_axl
3422  * number_of_submodules_trs
3423  * number_of_submodules_axl
3424  * number_of_crystals_trs
3425  * number_of_crystals_axl;
3426  }
3427  else
3428  for(int l=0 ; l<number_of_layers ; l++)
3429  {
3430  int32_t nb_crystals_layer = number_of_rsectors_ang
3431  * number_of_rsectors_axl
3432  * number_of_modules_trs
3433  * number_of_modules_axl
3434  * number_of_submodules_trs
3435  * number_of_submodules_axl
3436  * number_of_crystals_trs
3437  * number_of_crystals_axl;
3438 
3439  // Add layer elements if repeaters have been used on layers
3440  if(number_of_lyr_elts_trs.size()>0 || number_of_lyr_elts_axl.size()>0 )
3441  nb_crystals_layer *= number_of_lyr_elts_trs[l] * number_of_lyr_elts_axl[l];
3442 
3443  number_of_elements += nb_crystals_layer;
3444  }
3445 
3446 
3447  // Compute sizes and gaps of each cylindrical PET structures.
3448 
3449  // Compute crystal gaps
3450  if (crystal_step_axl - crystal_size_axl >= 0)
3451  crystal_gap_axl = crystal_step_axl - crystal_size_axl;
3452 
3453  if (crystal_step_trs - crystal_size_trs >= 0)
3454  crystal_gap_trs = crystal_step_trs - crystal_size_trs;
3455 
3456  // Compute submodule size from crystals size, gaps & number
3457  submodule_size_axl = crystal_size_axl*number_of_crystals_axl + crystal_gap_axl*(number_of_crystals_axl-1);
3458  submodule_size_trs = crystal_size_trs*number_of_crystals_trs + crystal_gap_trs*(number_of_crystals_trs-1);
3459 
3460  // Compute submodule gaps
3461  if (submodule_step_axl - submodule_size_axl >= 0)
3462  submodule_gap_axl = submodule_step_axl - submodule_size_axl;
3463 
3464  if (submodule_step_trs - submodule_size_trs >= 0)
3465  submodule_gap_trs = submodule_step_trs - submodule_size_trs;
3466 
3467  // Compute module size from submodule size, gaps & number
3468  module_size_axl = submodule_size_axl*number_of_submodules_axl + submodule_gap_axl*(number_of_submodules_axl-1);
3469  module_size_trs = submodule_size_trs*number_of_submodules_trs + submodule_gap_trs*(number_of_submodules_trs-1);
3470 
3471  // Compute module gaps
3472  if (module_step_axl - module_size_axl >= 0)
3473  module_gap_axl = module_step_axl - module_size_axl;
3474 
3475  if (module_step_trs - module_size_trs >= 0)
3476  module_gap_trs = module_step_trs - module_size_trs;
3477 
3478  // Compute rsector size from crystals size, gaps & number
3479  rsector_size_axl = module_size_axl*number_of_modules_axl + module_gap_axl*(number_of_modules_axl-1);
3480  rsector_size_trs = module_size_trs*number_of_modules_trs + module_gap_trs*(number_of_modules_trs-1);
3481 
3482 
3483  // Compute rsector gaps.
3484  if (rsector_step_axl - rsector_size_axl >= 0)
3485  rsector_gap_axl = rsector_step_axl - rsector_size_axl;
3486 
3487 
3488  // Axial FOV
3489  fov_axl = rsector_size_axl * number_of_rsectors_axl // rsectors
3490  + (number_of_rsectors_axl-1)*rsector_gap_axl; // gaps
3491 
3492  // Just use twice the total number of crystals to arbitrary define a number of voxels
3493  //voxels_number_axl = fov_axl/4 + 1;
3494  voxels_number_axl = ( number_of_crystals_axl
3495  * number_of_submodules_axl
3496  * number_of_modules_axl
3497  * number_of_rsectors_axl )
3498  * 2;
3499 
3500  // Transaxial FOV defined as half diameter/1.5
3501  fov_trs = (2*scanner_radius ) / 1.5;
3502 
3503  // Arbitrary define the transaxial voxel size as axial_voxel_size / 1.5
3504  //voxels_number_trs = fov_trs/4 + 1;
3505  voxels_number_trs = ( number_of_crystals_trs
3506  * number_of_submodules_trs
3507  * number_of_modules_trs
3508  * number_of_rsectors_ang )
3509  / 3;
3510 
3511 
3512  // Compute first angle
3513  // CASToR x and y axis for rotation inverted in comparison with GATE
3514  rsector_first_angle -= round(atan2f(rsector_pos_X , rsector_pos_Y) * 180. / M_PI);
3515 
3516  // Computing the other parts of variables, if their is more than one layer
3517  if (number_of_layers > 0)
3518  {
3519  for (int l=0; l < number_of_layers ; l++)
3520  {
3521  // Scanner vectors
3522  vec_scanner_radius.push_back( toString(scanner_radius
3523  +layers_positions[ l ][ 0 ]
3524  -layers_size_depth[ l ] / 2. ) );
3525 
3526  // Rsector vectors
3527  vec_number_of_rsectors_ang.push_back( toString(number_of_rsectors_ang) );
3528  vec_number_of_rsectors_axl.push_back( toString(number_of_rsectors_axl) );
3529  vec_rsector_gap_axl.push_back( toString(rsector_gap_axl) );
3530  vec_rsector_first_angle.push_back( toString(rsector_first_angle) );
3531 
3532  // Module vectors
3533  vec_number_of_modules_trs.push_back( toString(number_of_modules_trs) );
3534  vec_number_of_modules_axl.push_back( toString(number_of_modules_axl) );
3535  vec_module_gap_trs.push_back( toString(module_gap_trs) );
3536  vec_module_gap_axl.push_back( toString(module_gap_axl) );
3537 
3538  // Submodule vectors
3539  vec_number_of_submodules_trs.push_back( toString(number_of_submodules_trs) );
3540  vec_number_of_submodules_axl.push_back( toString(number_of_submodules_axl) );
3541  vec_submodule_gap_trs.push_back( toString(submodule_gap_trs) );
3542  vec_submodule_gap_axl.push_back( toString(submodule_gap_axl) );
3543 
3544  // Crystal vectors
3545  uint32_t nb_tot_trs_cry = (number_of_lyr_elts_trs.size()>0) ?
3546  number_of_lyr_elts_trs[l]*number_of_crystals_trs :
3547  number_of_crystals_trs ;
3548 
3549  uint32_t nb_tot_axl_cry = (number_of_lyr_elts_axl.size()>0) ?
3550  number_of_lyr_elts_axl[l]*number_of_crystals_axl :
3551  number_of_crystals_axl ;
3552 
3553  vec_number_of_crystals_trs.push_back( toString(nb_tot_trs_cry) );
3554  vec_number_of_crystals_axl.push_back( toString(nb_tot_axl_cry) );
3555 
3556 
3557  // Compute the gap from the layer variables
3558  // If those variables have not been set, use the crystals gap previously computed
3559  if(layers_step_trs.size()>0 ||
3560  layers_step_axl.size()>0)
3561  {
3562  if (layers_step_trs[l] - layers_size_trs[l] >= 0)
3563  crystal_gap_trs = layers_step_trs[l] - layers_size_trs[l];
3564 
3565  if (layers_step_axl[l] - layers_size_axl[l] >= 0)
3566  crystal_gap_axl = layers_step_axl[l] - layers_size_axl[l];
3567  }
3568 
3569  vec_crystal_gap_trs.push_back( toString(crystal_gap_trs) );
3570  vec_crystal_gap_axl.push_back( toString(crystal_gap_axl) );
3571 
3572  // Optionnal
3573  vec_mean_depth_of_interaction.push_back( toString(mean_depth_of_interaction) );
3574  vec_min_angle_diff.push_back( toString(min_angle_diff) );
3575  }
3576  }
3577  else
3578  {
3579  // Layer dimensions = crystal dimensions
3580  layers_size_depth.push_back( crystal_size_depth );
3581  layers_size_trs.push_back( crystal_size_trs );
3582  layers_size_axl.push_back( crystal_size_axl );
3583 
3584 
3585  // Scanner vectors
3586  vec_scanner_radius.push_back( toString(scanner_radius - crystal_size_depth/2) );
3587 
3588  // Rsector vectors
3589  vec_number_of_rsectors_ang.push_back( toString(number_of_rsectors_ang) );
3590  vec_number_of_rsectors_axl.push_back( toString(number_of_rsectors_axl) );
3591  vec_rsector_gap_axl.push_back( toString(rsector_gap_axl) );
3592  vec_rsector_first_angle.push_back( toString(rsector_first_angle) );
3593 
3594  // Module vectors
3595  vec_number_of_modules_trs.push_back( toString(number_of_modules_trs) );
3596  vec_number_of_modules_axl.push_back( toString(number_of_modules_axl) );
3597  vec_module_gap_trs.push_back( toString(module_gap_trs) );
3598  vec_module_gap_axl.push_back( toString(module_gap_axl) );
3599 
3600  // Submodule vectors
3601  vec_number_of_submodules_trs.push_back( toString(number_of_submodules_trs) );
3602  vec_number_of_submodules_axl.push_back( toString(number_of_submodules_axl) );
3603  vec_submodule_gap_trs.push_back( toString(submodule_gap_trs) );
3604  vec_submodule_gap_axl.push_back( toString(submodule_gap_axl) );
3605 
3606  // Crystal vectors
3607  vec_number_of_crystals_trs.push_back( toString(number_of_crystals_trs) );
3608  vec_number_of_crystals_axl.push_back( toString(number_of_crystals_axl) );
3609  vec_crystal_gap_trs.push_back( toString(crystal_gap_trs) );
3610  vec_crystal_gap_axl.push_back( toString(crystal_gap_axl) );
3611 
3612  // Optionnal
3613  vec_mean_depth_of_interaction.push_back( toString(mean_depth_of_interaction) );
3614  vec_min_angle_diff.push_back( toString(min_angle_diff) );
3615 
3616  // Update the real number of layers
3617  number_of_layers = 1;
3618  }
3619 
3620 
3621  // Write the .geom file
3622  ofstream fileGeom(a_pathGeom.c_str(), ios::out | ios::trunc);
3623  if(fileGeom)
3624  {
3625  fileGeom <<"# comments" << endl;
3626  fileGeom <<"# Y _________ "<< endl;
3627  fileGeom <<"# | / _ \\ \\ "<< endl;
3628  fileGeom <<"# | | / \\ | |"<< endl;
3629  fileGeom <<"# |_____ Z | | | | |"<< endl;
3630  fileGeom <<"# \\ | | | | |" << endl;
3631  fileGeom <<"# \\ | \\_/ | |" << endl;
3632  fileGeom <<"# X \\___/_____/" << endl;
3633  fileGeom <<"# Left-handed axis orientation"<< endl;
3634  fileGeom <<"# scanner axis is z" << endl;
3635  fileGeom <<"# positions in millimeters"<< endl;
3636  fileGeom <<"# Use comma without space as separator in the tables." << endl;
3637 
3638  fileGeom << ""<< endl;
3639 
3640  // Mandatory fields
3641  fileGeom << "# MANDATORY FIELDS"<< endl;
3642  fileGeom << "modality : " << modality << endl;
3643  fileGeom << "scanner name : " << scanner_name << endl;
3644  fileGeom << "number of elements : " << number_of_elements << endl;
3645  fileGeom << "number of layers : " << number_of_layers << endl;
3646  fileGeom << "" << endl;
3647  fileGeom << "voxels number transaxial : " << voxels_number_trs << endl;
3648  fileGeom << "voxels number axial : " << voxels_number_axl << endl;
3649 
3650  fileGeom << "field of view transaxial : " << fov_trs << endl;
3651  fileGeom << "field of view axial : " << fov_axl << endl << endl;
3652  fileGeom << "description : " << description << endl;
3653  fileGeom << "" << endl;
3654  WriteVector(fileGeom,"scanner radius : ",vec_scanner_radius);
3655  WriteVector(fileGeom,"number of rsectors : ",vec_number_of_rsectors_ang);
3656  WriteVector(fileGeom,"number of crystals transaxial : ",vec_number_of_crystals_trs);
3657  WriteVector(fileGeom, "number of crystals axial : ",vec_number_of_crystals_axl);
3658  fileGeom << ""<< endl;
3659  WriteVector(fileGeom, "crystals size depth : ", layers_size_depth);
3660  WriteVector(fileGeom, "crystals size transaxial : ", layers_size_trs);
3661  WriteVector(fileGeom, "crystals size axial : ", layers_size_axl);
3662  fileGeom << ""<< endl;
3663  fileGeom << ""<< endl;
3664 
3665  // Optional fields
3666  fileGeom << "# OPTIONAL FIELDS"<< endl;
3667  WriteVector(fileGeom,"rsectors first angle : ",vec_rsector_first_angle);
3668  WriteVector(fileGeom, "number of rsectors axial : ",vec_number_of_rsectors_axl);
3669  WriteVector(fileGeom,"rsector gap transaxial : ",vec_rsector_gap_trs);
3670  WriteVector(fileGeom,"rsector gap axial : ",vec_rsector_gap_axl);
3671  WriteVector(fileGeom,"number of modules transaxial : ",vec_number_of_modules_trs);
3672  WriteVector(fileGeom, "number of modules axial : ",vec_number_of_modules_axl);
3673  WriteVector(fileGeom,"module gap transaxial : ",vec_module_gap_trs);
3674  WriteVector(fileGeom,"module gap axial : ",vec_module_gap_axl);
3675  WriteVector(fileGeom,"number of submodules transaxial : ",vec_number_of_submodules_trs);
3676  WriteVector(fileGeom, "number of submodules axial : ",vec_number_of_submodules_axl);
3677  WriteVector(fileGeom,"submodule gap transaxial : ",vec_submodule_gap_trs);
3678  WriteVector(fileGeom,"submodule gap axial : ",vec_submodule_gap_axl);
3679  WriteVector(fileGeom,"crystal gap transaxial : ",vec_crystal_gap_trs);
3680  WriteVector(fileGeom,"crystal gap axial : ",vec_crystal_gap_axl);
3681  WriteVector(fileGeom, "mean depth of interaction : ", vec_mean_depth_of_interaction);
3682  fileGeom << "rotation direction : CCW " << endl; // default for GATE
3683  fileGeom << ""<< endl;
3684 
3685  // Write only if different from default values
3686  if(min_angle_diff > 0.) fileGeom << "min angle difference : " << min_angle_diff << endl;
3687 
3688  // Convert angular span to the CASToR convention
3689  if(rsector_angular_span >= 360.0005 ||
3690  rsector_angular_span <= 359.9995 ) // GATE bounds
3691  {
3692  rsector_angular_span *= (double)number_of_rsectors_ang/(double)(number_of_rsectors_ang-1);
3693  fileGeom << "rsectors angular span : " << rsector_angular_span << endl;
3694  }
3695 
3696  if(rsector_nb_zshifts > 0) fileGeom << "rsectors nbZShift :" << rsector_nb_zshifts << endl;
3697  if(!vec_rsector_Z_Shift.empty()) WriteVector(fileGeom, "rsectors ZShift : ", vec_rsector_Z_Shift);
3698 
3699  fileGeom.close();
3700 
3701  cout << "Output geom file written at :" << a_pathGeom << endl;
3702  }
3703  else
3704  {
3705  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Couldn't open geom file for writing "<< a_pathGeom << " !" << endl);
3706  return 1;
3707  }
3708 
3709 
3710 
3711  return 0;
3712 }
3713 
3714 
3715 
3716 // =====================================================================
3717 // ---------------------------------------------------------------------
3718 // ---------------------------------------------------------------------
3719 // =====================================================================
3720 /*
3721  \fn CreateGeomWithECAT()
3722  \param a_pathMac : string containing the path to a GATE macro file
3723  \param a_pathGeom : string containing the path to a CASToR output geom file
3724  \brief Read a GATE macro file containing the description of an ecat system, and convert it to a geom file
3725  \return 0 if success, positive value otherwise
3726 */
3727 int CreateGeomWithECAT(string a_pathMac, string a_pathGeom)
3728 {
3729  /* Declaring variables */
3730 
3731  // Scanner
3732  string modality;
3733  string scanner_name = GetFileFromPath(a_pathGeom.substr(0,a_pathGeom.find_first_of(".geom")));
3734  double scanner_radius = 0.;
3735  uint32_t number_of_elements = 0;
3736  string description = "ECAT system extracted from GATE macro: " + a_pathMac;
3737 
3738  // blocks
3739  string block_name = "block";
3740  uint32_t number_of_blocks = 1;
3741  uint32_t number_of_blocks_trs = 1;
3742  uint32_t number_of_blocks_axl = 1;
3743  double block_step_trs = 0.;
3744  double block_step_axl = 0.;
3745  double block_gap_trs = 0.;
3746  double block_gap_axl = 0.;
3747  double block_size_Y;
3748  double block_size_Z;
3749  double block_pos_X = 0.;
3750  double block_pos_Y = 0.;
3751  double block_first_angle = 0.;
3752  double block_angular_span = 360.;
3753  uint32_t block_nb_zshifts = 0;
3754  vector <double> vec_block_Z_Shift;
3755  bool is_block_Y_axis = false;
3756 
3757 
3758 
3759  // Crystals
3760  string crystal_name = "crystal";
3761  uint32_t number_of_crystals_trs = 1;
3762  uint32_t number_of_crystals_axl = 1;
3763  double crystal_step_trs = 0.;
3764  double crystal_step_axl = 0.;
3765  double crystal_gap_trs = 0.;
3766  double crystal_gap_axl = 0.;
3767  double crystal_size_depth = 0.;
3768  double crystal_size_trs = 0.;
3769  double crystal_size_axl = 0.;
3770 
3771  // Optional
3772  uint32_t voxels_number_trs;
3773  uint32_t voxels_number_axl;
3774  double fov_trs;
3775  double fov_axl;
3776  double min_angle_diff = 0.;
3777 
3778  vector<string> path_mac_files;
3779  path_mac_files.push_back(a_pathMac);
3780 
3781  // Recover path to all macro files from the main mac file
3782  if(GetGATEMacFiles(a_pathMac , path_mac_files))
3783  {
3784  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()->Error occurred when trying to recover paths to GATE macro files !" << endl);
3785  return 1;
3786  }
3787 
3788 
3789  // Recover aliases of the different parts of the architecture
3790  if(GetGATEAliasesEcat(path_mac_files, block_name, crystal_name, 2) )
3791  {
3792  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()->Error occurred when trying to recover aliases for the elements of the ecat !" << endl);
3793  return 1;
3794  }
3795 
3796 
3797  // Loop to recover all other informations
3798  for(uint16_t f=0 ; f<path_mac_files.size() ; f++)
3799  {
3800  ifstream systemMac(path_mac_files[f].c_str(), ios::in);
3801 
3802  string line;
3803  while(getline(systemMac, line))
3804  {
3805  // Scanner
3806  modality = "PET";
3807  vector <string> values;
3808  string entry = "";
3809 
3810 
3811  entry = "/gate/"+block_name+"/placement/setTranslation";
3812  values = CheckGATECommand(entry, line);
3813  if (values.size()>0)
3814  {
3815  if(ConvertFromString(values[0], &block_pos_X) ||
3816  ConvertFromString(values[1], &block_pos_Y) )
3817  {
3818  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
3819  return 1;
3820  }
3821 
3822  // Check first block cartesian coordinates is 0 on X or Y axis
3823  // Throw error otherwise
3824  if(block_pos_X!=0 && block_pos_Y !=0)
3825  {
3826  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
3827  Cerr(" Block cartesian coordinates on either the X or Y axis expected to be equal to 0 "<< endl);
3828  return 1;
3829  }
3830 
3831  // Get the axis on which the first rsector is positionned
3832  if(block_pos_Y!=0) is_block_Y_axis = true;
3833 
3834  scanner_radius += is_block_Y_axis ? abs(block_pos_Y) : abs(block_pos_X) ;
3835  }
3836 
3837 
3838  // Adjust scanner radius (from center to block surface) according to block size
3839  /*
3840  entry = is_block_Y_axis ?
3841  "/gate/"+block_name+"/geometry/setYLength" :
3842  "/gate/"+block_name+"/geometry/setXLength";
3843 
3844  values = CheckGATECommand(entry, line);
3845  if (values.size()>0)
3846  {
3847  double block_size = 0.;
3848  if(ConvertFromString(values[0], &block_size) )
3849  {
3850  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3851  return 1;
3852  }
3853 
3854  scanner_radius -= block_size/2;
3855  }
3856  */
3857 
3858 
3859  entry = "/gate/"+block_name+"/geometry/setZLength";
3860  values = CheckGATECommand(entry, line);
3861  if (values.size()>0)
3862  {
3863  if(ConvertFromString(values[0], &fov_axl))
3864  {
3865  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
3866  return 1;
3867  }
3868  // 4mm voxels by default
3869  voxels_number_axl = fov_axl/4 + 1;
3870  }
3871 
3872 
3873  // blocks
3874  entry = "/gate/"+block_name+"/ring/setRepeatNumber";
3875  values = CheckGATECommand(entry, line);
3876  if (values.size()>0)
3877  {
3878  if(ConvertFromString(values[0], &number_of_blocks))
3879  {
3880  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
3881  return 1;
3882  }
3883  }
3884 
3885 
3886  entry = "/gate/"+block_name+"/ring/setFirstAngle";
3887  values = CheckGATECommand(entry, line);
3888  if (values.size()>0)
3889  {
3890  if(ConvertFromString(values[0], &block_first_angle))
3891  {
3892  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
3893  return 1;
3894  }
3895  }
3896 
3897 
3898  entry = "/gate/"+block_name+"/ring/setAngularSpan";
3899  values = CheckGATECommand(entry, line);
3900  if (values.size()>0)
3901  {
3902  if(ConvertFromString(values[0], &block_angular_span))
3903  {
3904  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
3905  return 1;
3906  }
3907  }
3908 
3909  entry = "/gate/"+block_name+"/ring/setModuloNumber";
3910  values = CheckGATECommand(entry, line);
3911  if (values.size()>0)
3912  {
3913  if(ConvertFromString(values[0], &block_nb_zshifts) )
3914  {
3915  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
3916  return 1;
3917  }
3918  }
3919 
3920  for (int i=1; i<8; i++)
3921  {
3922  entry = "/gate/"+block_name+"/ring/setZShift"+toString(i);
3923  values = CheckGATECommand(entry, line);
3924  if (values.size()>0)
3925  {
3926  double zshift;
3927  if(ConvertFromString(values[0], &zshift))
3928  {
3929  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
3930  return 1;
3931  }
3932  vec_block_Z_Shift.push_back(zshift);
3933  }
3934  }
3935 
3936 
3937 
3938 
3939  // Modules
3940  entry = "/gate/"+block_name+"/linear/setRepeatNumber";
3941  values = CheckGATECommand(entry, line);
3942  if (values.size()>0)
3943  {
3944  if(ConvertFromString(values[0], &number_of_blocks_axl))
3945  {
3946  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
3947  return 1;
3948  }
3949  }
3950 
3951 
3952  entry = "/gate/"+block_name+"/linear/setRepeatVector";
3953  values = CheckGATECommand(entry, line);
3954  if (values.size()>0)
3955  {
3956  if(ConvertFromString(values[2], &block_step_axl))
3957  {
3958  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
3959  return 1;
3960  }
3961  }
3962 
3963 
3964 
3965  // Crystals
3966 
3967  entry = "/gate/"+crystal_name+"/placement/setTranslation";
3968  values = CheckGATECommand(entry, line);
3969 
3970  FLTNB crystal_pos_X =0.,
3971  crystal_pos_Y =0.;
3972 
3973  if (values.size()>0)
3974  {
3975  if(ConvertFromString(values[0], &crystal_pos_X) ||
3976  ConvertFromString(values[1], &crystal_pos_Y) )
3977  {
3978  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
3979  return 1;
3980  }
3981 
3982  // Check first crystal cartesian coordinates is 0 on X or Y axis
3983  // Throw error otherwise
3984  if(crystal_pos_X!=0 && crystal_pos_Y !=0)
3985  {
3986  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
3987  Cerr(" Block cartesian coordinates on either the X or Y axis expected to be equal to 0 "<< endl);
3988  return 1;
3989  }
3990 
3991  scanner_radius += is_block_Y_axis ? abs(crystal_pos_Y) : abs(crystal_pos_X) ;
3992  }
3993 
3994  // Adjust scanner radius (from center to block surface) according to crystal size
3995  entry = is_block_Y_axis ?
3996  "/gate/"+crystal_name+"/geometry/setYLength" :
3997  "/gate/"+crystal_name+"/geometry/setXLength";
3998 
3999  values = CheckGATECommand(entry, line);
4000  if (values.size()>0)
4001  {
4002  double crystal_size = 0.;
4003  if(ConvertFromString(values[0], &crystal_size) )
4004  {
4005  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4006  return 1;
4007  }
4008 
4009  scanner_radius -= crystal_size/2;
4010  }
4011 
4012 
4013 
4014  // assumes that first block is positionned on the x-axis
4015  entry = is_block_Y_axis ?
4016  "/gate/"+crystal_name+"/cubicArray/setRepeatNumberX":
4017  "/gate/"+crystal_name+"/cubicArray/setRepeatNumberY";
4018 
4019  values = CheckGATECommand(entry, line);
4020  if (values.size()>0)
4021  {
4022  if(ConvertFromString(values[0], &number_of_crystals_trs))
4023  {
4024  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4025  return 1;
4026  }
4027  }
4028 
4029  entry = "/gate/"+crystal_name+"/cubicArray/setRepeatNumberZ";
4030  values = CheckGATECommand(entry, line);
4031  if (values.size()>0)
4032  {
4033  if(ConvertFromString(values[0], &number_of_crystals_axl))
4034  {
4035  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4036  return 1;
4037  }
4038  }
4039 
4040 
4041  entry = "/gate/"+crystal_name+"/cubicArray/setRepeatVector";
4042  values = CheckGATECommand(entry, line);
4043  if (values.size()>0)
4044  {
4045  string trs_step = is_block_Y_axis ?
4046  values[0] :
4047  values[1] ;
4048 
4049  if(ConvertFromString(values[1], &crystal_step_trs) ||
4050  ConvertFromString(values[2], &crystal_step_axl) )
4051  {
4052  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4053  return 1;
4054  }
4055  }
4056 
4057 
4058  // assumes that first block is positionned on the x-axis
4059  entry = "/gate/"+crystal_name+"/geometry/setXLength";
4060  values = CheckGATECommand(entry, line);
4061  if (values.size()>0)
4062  {
4063  double x_length;
4064  if(ConvertFromString(values[0], &x_length))
4065  {
4066  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4067  return 1;
4068  }
4069 
4070  if (is_block_Y_axis)
4071  crystal_size_trs = x_length;
4072  else
4073  crystal_size_depth = x_length;
4074  }
4075 
4076 
4077  // assumes that first block is positionned on the x-axis
4078  entry = "/gate/"+crystal_name+"/geometry/setYLength";
4079  values = CheckGATECommand(entry, line);
4080  if (values.size()>0)
4081  {
4082  double y_length;
4083  if(ConvertFromString(values[0], &y_length))
4084  {
4085  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4086  return 1;
4087  }
4088 
4089  if (is_block_Y_axis)
4090  crystal_size_depth = y_length;
4091  else
4092  crystal_size_trs = y_length;
4093  }
4094 
4095  entry = "/gate/"+crystal_name+"/geometry/setZLength";
4096  values = CheckGATECommand(entry, line);
4097  if (values.size()>0)
4098  {
4099  if(ConvertFromString(values[0], &crystal_size_axl))
4100  {
4101  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4102  return 1;
4103  }
4104  }
4105 
4106 
4107 
4108 
4109  entry = "/gate/digitizer/Coincidences/minSectorDifference";
4110  values = CheckGATECommand(entry, line);
4111  if (values.size()>0)
4112  {
4113  double min_sector_diff;
4114  if(ConvertFromString(values[0], &min_sector_diff))
4115  {
4116  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4117  return 1;
4118  }
4119  min_angle_diff = 360/number_of_blocks*min_sector_diff;
4120  }
4121 
4122  }
4123  systemMac.close();
4124  }
4125 
4126  number_of_elements = number_of_blocks *
4127  number_of_blocks_axl *
4128  number_of_crystals_trs *
4129  number_of_crystals_axl;
4130 
4131 
4132 
4133  // Transaxial FOV defined as scanner radius / 2
4134  fov_trs = (2*scanner_radius ) / 1.5;
4135 
4136  // 4mm voxels by default
4137  voxels_number_trs = ( number_of_crystals_trs
4138  * number_of_blocks_trs)
4139  / 3;
4140 
4141 
4142  if (crystal_step_axl - crystal_size_axl >= 0)
4143  crystal_gap_axl = crystal_step_axl - crystal_size_axl;
4144 
4145  if (crystal_step_trs - crystal_size_trs >= 0)
4146  crystal_gap_trs = crystal_step_trs - crystal_size_trs;
4147 
4148 
4149  // Compute submodule size from crystals size, gaps & number
4150  block_size_Z = crystal_size_axl*number_of_crystals_axl + crystal_gap_axl*(number_of_crystals_axl-1);
4151  block_size_Y = crystal_size_trs*number_of_crystals_trs + crystal_gap_trs*(number_of_crystals_trs-1);
4152 
4153  // Compute submodule gaps
4154  if (block_step_axl - block_size_Z >= 0)
4155  block_gap_axl = block_step_axl - block_size_Z;
4156 
4157  if (block_step_trs - block_size_Y >= 0)
4158  block_gap_trs = block_step_trs - block_size_Y;
4159 
4160  // CASToR x and y axis for rotation inverted in comparison with GATE
4161  block_first_angle -= round(atan2f(block_pos_X , block_pos_Y) * 180. / M_PI);
4162 
4163 
4164  // Writing the .geom file
4165  ofstream fileGeom(a_pathGeom.c_str(), ios::out | ios::trunc);
4166 
4167  if(fileGeom)
4168  {
4169  fileGeom <<"# comments" << endl;
4170  fileGeom <<"# Y _________ "<< endl;
4171  fileGeom <<"# | / _ \\ \\ "<< endl;
4172  fileGeom <<"# | | / \\ | |"<< endl;
4173  fileGeom <<"# |_____ Z | | | | |"<< endl;
4174  fileGeom <<"# \\ | | | | |" << endl;
4175  fileGeom <<"# \\ | \\_/ | |" << endl;
4176  fileGeom <<"# X \\___/_____/" << endl;
4177  fileGeom <<"# Left-handed axis orientation"<< endl;
4178  fileGeom <<"# scanner axis is z" << endl;
4179  fileGeom <<"# positions in millimeters"<< endl;
4180  fileGeom <<"# Use comma without space as separator in the tables." << endl;
4181 
4182 
4183  fileGeom << "modality : " << modality << endl;
4184  fileGeom << "scanner name : " << scanner_name << endl;
4185  fileGeom << "number of elements : " << number_of_elements << endl;
4186  fileGeom << "number of layers : " << "1" << endl;
4187  fileGeom << "" << endl;
4188  fileGeom << "# default reconstruction parameters" << endl;
4189  fileGeom << "voxels number transaxial : " << voxels_number_trs << endl;
4190  fileGeom << "voxels number axial : " << voxels_number_axl << endl;
4191 
4192  fileGeom << "field of view transaxial : " << fov_trs << endl;
4193  fileGeom << "field of view axial : " << fov_axl << endl;
4194  fileGeom << "" << endl;
4195  fileGeom << "description : " << description << endl;
4196  fileGeom << "" << endl;
4197  fileGeom << "scanner radius : " << scanner_radius << endl;
4198  fileGeom << "number of rsectors : " << number_of_blocks << endl;
4199  fileGeom << "number of crystals transaxial : " << number_of_crystals_trs << endl;
4200  fileGeom << "number of crystals axial : " << number_of_crystals_axl << endl;
4201 
4202  fileGeom << ""<< endl;
4203  fileGeom << "# The 4 following parameters could be defined in arrays (SizeLayer1,SizeLayer2,SizeLayer3,etc..) if their is more than one layer"<< endl;
4204  fileGeom << "crystals size depth : " << crystal_size_depth << endl;
4205  fileGeom << "crystals size transaxial : " << crystal_size_trs << endl;
4206  fileGeom << "crystals size axial : " << crystal_size_axl << endl;
4207  fileGeom << ""<< endl;
4208 
4209  // Optional fields
4210  fileGeom << "rsectors first angle : " << block_first_angle << endl;
4211  fileGeom << "number of modules transaxial : " << number_of_blocks_trs << endl;
4212  fileGeom << "number of modules axial : " << number_of_blocks_axl << endl;
4213  fileGeom << "module gap transaxial : " << block_gap_trs << endl;
4214  fileGeom << "module gap axial : " << block_gap_axl << endl;
4215  fileGeom << "crystal gap transaxial : " << crystal_gap_trs << endl;
4216  fileGeom << "crystal gap axial : " << crystal_gap_axl << endl;
4217  fileGeom << "rotation direction : CCW " << endl; // default for GATE
4218  fileGeom << ""<< endl;
4219 
4220  // Write only if different from default values
4221  if(min_angle_diff > 0.) fileGeom << "min angle difference : " << min_angle_diff << endl;
4222 
4223  // Convert angular span to the CASToR convention
4224  if(block_angular_span >= 360.0005 ||
4225  block_angular_span <= 359.9995 ) // GATE bounds
4226  {
4227  block_angular_span *= (double)(number_of_blocks)/(double)(number_of_blocks-1);
4228  fileGeom << "rsectors angular span : " << block_angular_span << endl;
4229  }
4230 
4231  if(block_nb_zshifts > 0) fileGeom << "rsectors nbZShift :" << block_nb_zshifts << endl;
4232  if(!vec_block_Z_Shift.empty()) WriteVector(fileGeom, "rsectors ZShift : ", vec_block_Z_Shift);
4233 
4234  fileGeom.close();
4235 
4236  Cout("Output geom file written at :" << a_pathGeom << endl);
4237  }
4238  else
4239  {
4240  Cerr("***** dataConversionUtilities::CreateGeomWithECAT()-> Couldn't open geom file for writing "<< a_pathGeom << " !" << endl);
4241  return 1;
4242  }
4243 
4244  return 0;
4245 }
4246 
4247 
4248 
4249 
4250 // =====================================================================
4251 // ---------------------------------------------------------------------
4252 // ---------------------------------------------------------------------
4253 // =====================================================================
4254 /*
4255  \fn CreateGeomWithSPECT()
4256  \param a_pathMac : string containing the path to a GATE macro file
4257  \param a_pathGeom : string containing the path to a CASToR output geom file
4258  \brief Read a GATE macro file containing the description of a SPECThead system, and convert it to a geom file
4259  \return 0 if success, positive value otherwise
4260 */
4261 int CreateGeomWithSPECT(string a_pathMac, string a_pathGeom)
4262 {
4263  /* Declaring variables */
4264  string modality;
4265  string scanner_name = GetFileFromPath(a_pathGeom.substr(0,a_pathGeom.find_first_of(".geom")));
4266  FLTNB scanner_radius = -1.;
4267  string description = "SPECT camera extracted from GATE macro: " + a_pathMac;
4268 
4269  // head(s)
4270  string head_name = "SPECThead"; // not used. Correspond to SPECThead
4271  uint32_t number_of_heads = 0;
4272  FLTNB head_pos_X = 0.;
4273  FLTNB head_pos_Y = 0.;
4274  FLTNB head_first_angle = 0.;
4275  FLTNB head_angular_pitch = -1.;
4276  string head_orbit_name = "";
4277  FLTNB head_rotation_speed = 0.;
4278  bool is_head_Y_axis = false;
4279 
4280  // crystal
4281  string crystal_name = "crystal";
4282  FLTNB crystal_size_trs = 0.;
4283  FLTNB crystal_size_axl = 0.;
4284  FLTNB crystal_depth = 0;
4285 
4286  // pixel
4287  string pixel_name = "pixel";
4288  uint32_t number_of_pixels_trs = 1;
4289  uint32_t number_of_pixels_axl = 1;
4290  FLTNB pix_size_trs = 0.;
4291  FLTNB pix_size_axl = 0.;
4292  FLTNB pix_step_trs = 0.;
4293  FLTNB pix_step_axl = 0.;
4294  FLTNB pix_gap_trs = 0.;
4295  FLTNB pix_gap_axl = 0.;
4296 
4297  // collimator parameters
4298  string focal_model_trs = "constant";
4299  uint16_t nb_coeff_model_trs = 1;
4300  FLTNB coeff_model_trs = 0.;
4301  string focal_model_axl = "constant";
4302  uint16_t nb_coeff_model_axl = 1;
4303  FLTNB coeff_model_axl = 0.;
4304 
4305  // others
4306  uint32_t voxels_number_trs;
4307  uint32_t voxels_number_axl;
4308  double fov_trs;
4309  double fov_axl;
4310 
4311  vector<string> path_mac_files;
4312  path_mac_files.push_back(a_pathMac);
4313 
4314  // Recover path to all macro files from the main mac file
4315  if(GetGATEMacFiles(a_pathMac , path_mac_files))
4316  {
4317  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()->Error occurred when trying to recover paths to GATE macro files !" << endl);
4318  return 1;
4319  }
4320 
4321 
4322  // Recover aliases of the different parts of the architecture
4323  if(GetGATEAliasesSPECT(path_mac_files, head_name, crystal_name, pixel_name, 2) )
4324  {
4325  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()->Error occurred when trying to recover aliases for the elements of the SPECThead !" << endl);
4326  return 1;
4327  }
4328 
4329  // Loop to recover all other informations
4330  for(uint16_t f=0 ; f<path_mac_files.size() ; f++)
4331  {
4332  ifstream systemMac(path_mac_files[f].c_str(), ios::in);
4333 
4334  string line;
4335  while(getline(systemMac, line))
4336  {
4337  // Scanner
4338  modality = "SPECT_CONVERGENT";
4339  vector <string> values;
4340  string entry = "";
4341 
4342  // assumes that first block is positionned on the x-axis
4343  entry = "/gate/"+head_name+"/placement/setTranslation";
4344  values = CheckGATECommand(entry, line);
4345  if (values.size()>0)
4346  {
4347  if(ConvertFromString(values[0], &head_pos_X) ||
4348  ConvertFromString(values[1], &head_pos_Y) )
4349  {
4350  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4351  return 1;
4352  }
4353 
4354  // Check first block cartesian coordinates is 0 on X or Y axis
4355  // Throw error otherwise
4356  if(head_pos_X!=0 && head_pos_Y !=0)
4357  {
4358  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4359  Cerr(" Block cartesian coordinates on either the X or Y axis expected to be equal to 0 "<< endl);
4360  return 1;
4361  }
4362 
4363  // Get the axis on which the first rsector is positionned
4364  if(head_pos_Y!=0) is_head_Y_axis = true;
4365 
4366  scanner_radius = is_head_Y_axis ? abs(head_pos_Y) : abs(head_pos_X) ;
4367  }
4368 
4369 
4370  entry = "/gate/"+head_name+"/geometry/setZLength";
4371  values = CheckGATECommand(entry, line);
4372  if (values.size()>0)
4373  {
4374  if(ConvertFromString(values[0], &fov_axl))
4375  {
4376  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4377  return 1;
4378  }
4379  // 4mm voxels by default
4380  voxels_number_axl = fov_axl/4 + 1;
4381  }
4382 
4383 
4384  entry = "/gate/"+head_name+"/ring/setRepeatNumber";
4385  values = CheckGATECommand(entry, line);
4386  if (values.size()>0)
4387  {
4388  if(ConvertFromString(values[0], &number_of_heads))
4389  {
4390  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4391  return 1;
4392  }
4393  }
4394 
4395 
4396  entry = "/gate/"+head_name+"/ring/setFirstAngle";
4397  values = CheckGATECommand(entry, line);
4398  if (values.size()>0)
4399  {
4400  if(ConvertFromString(values[0], &head_first_angle))
4401  {
4402  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4403  return 1;
4404  }
4405  }
4406 
4407 
4408  entry = "/gate/"+head_name+"/ring/setAngularPitch";
4409  values = CheckGATECommand(entry, line);
4410  if (values.size()>0)
4411  {
4412  if(ConvertFromString(values[0], &head_angular_pitch))
4413  {
4414  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4415  return 1;
4416  }
4417  }
4418 
4419  entry = "/gate/"+head_name+"/moves/insert";
4420  values = CheckGATECommand(entry, line);
4421  if (values.size()>0)
4422  {
4423  if(ConvertFromString(values[0], &head_orbit_name))
4424  {
4425  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4426  return 1;
4427  }
4428  }
4429 
4430  entry = "/gate/"+head_orbit_name+"/setSpeed";
4431  values = CheckGATECommand(entry, line);
4432  if (values.size()>0)
4433  {
4434  if(ConvertFromString(values[0], &head_rotation_speed))
4435  {
4436  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Conversion error occurred while trying to parse line: " << line<< endl);
4437  return 1;
4438  }
4439  }
4440 
4441 
4442 
4443  // --- Crystals ---
4444  entry = "/gate/"+crystal_name+"/geometry/setXLength";
4445  values = CheckGATECommand(entry, line);
4446  if (values.size()>0)
4447  {
4448  double x_length;
4449  if(ConvertFromString(values[0], &x_length) )
4450  {
4451  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4452  return 1;
4453  }
4454 
4455  if (is_head_Y_axis)
4456  crystal_size_trs = x_length;
4457  else
4458  crystal_depth = x_length;
4459  }
4460 
4461  entry = "/gate/"+crystal_name+"/geometry/setYLength";
4462  values = CheckGATECommand(entry, line);
4463  if (values.size()>0)
4464  {
4465  double y_length;
4466  if(ConvertFromString(values[0], &y_length) )
4467  {
4468  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4469  return 1;
4470  }
4471 
4472  if (is_head_Y_axis)
4473  crystal_depth = y_length;
4474  else
4475  crystal_size_trs = y_length;
4476 
4477  }
4478 
4479  entry = "/gate/"+crystal_name+"/geometry/setZLength";
4480  values = CheckGATECommand(entry, line);
4481  if (values.size()>0)
4482  {
4483  if(ConvertFromString(values[0], &crystal_size_axl) )
4484  {
4485  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4486  return 1;
4487  }
4488  }
4489 
4490 
4491 
4492 
4493 
4494  // --- Pixels ---
4495  entry = is_head_Y_axis ?
4496  "/gate/"+pixel_name+"/cubicArray/setRepeatNumberX":
4497  "/gate/"+pixel_name+"/cubicArray/setRepeatNumberY";
4498 
4499 
4500  values = CheckGATECommand(entry, line);
4501  if (values.size()>0)
4502  {
4503  if(ConvertFromString(values[0], &number_of_pixels_trs) )
4504  {
4505  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4506  return 1;
4507  }
4508  }
4509 
4510  entry = "/gate/"+pixel_name+"/cubicArray/setRepeatNumberZ";
4511  values = CheckGATECommand(entry, line);
4512  if (values.size()>0)
4513  {
4514  if(ConvertFromString(values[0], &number_of_pixels_axl) )
4515  {
4516  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4517  return 1;
4518  }
4519  }
4520 
4521 
4522  entry = "/gate/"+pixel_name+"/geometry/setXLength";
4523  values = CheckGATECommand(entry, line);
4524  if (values.size()>0)
4525  {
4526  double x_length;
4527  if(ConvertFromString(values[0], &x_length) )
4528  {
4529  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4530  return 1;
4531  }
4532 
4533  if (is_head_Y_axis)
4534  pix_size_trs = x_length;
4535  }
4536 
4537  entry = "/gate/"+pixel_name+"/geometry/setYLength";
4538  values = CheckGATECommand(entry, line);
4539  if (values.size()>0)
4540  {
4541  double y_length;
4542  if(ConvertFromString(values[0], &y_length) )
4543  {
4544  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4545  return 1;
4546  }
4547 
4548  if (!is_head_Y_axis)
4549  pix_size_trs = y_length;
4550 
4551  }
4552 
4553  entry = "/gate/"+pixel_name+"/geometry/setZLength";
4554  values = CheckGATECommand(entry, line);
4555  if (values.size()>0)
4556  {
4557  if(ConvertFromString(values[0], &pix_size_axl) )
4558  {
4559  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4560  return 1;
4561  }
4562  }
4563 
4564 
4565  entry = "/gate/"+pixel_name+"/cubicArray/setRepeatVector";
4566  values = CheckGATECommand(entry, line);
4567  if (values.size()>0)
4568  {
4569  string trs_step = is_head_Y_axis ?
4570  values[0] :
4571  values[1] ;
4572 
4573  if(ConvertFromString(trs_step, &pix_step_trs) ||
4574  ConvertFromString(values[2], &pix_step_axl) )
4575  {
4576  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4577  return 1;
4578  }
4579  }
4580 
4581 
4582  // collimator parameter
4583  entry = is_head_Y_axis ?
4584  "/gate/fanbeam/geometry/setFocalDistanceY":
4585  "/gate/fanbeam/geometry/setFocalDistanceX";
4586 
4587  entry = "/gate/fanbeam/geometry/setFocalDistanceX";
4588  values = CheckGATECommand(entry, line);
4589  if (values.size()>0)
4590  {
4591  if(ConvertFromString(values[0], &coeff_model_trs) )
4592  {
4593  Cerr("***** dataConversionUtilities::CreateGeomWithCylindrical()-> Conversion error occurred while trying to parse line: " << line<< endl);
4594  return 1;
4595  }
4596 
4597  focal_model_trs = "polynomial";
4598  }
4599 
4600  }
4601  systemMac.close();
4602  }
4603 
4604  // Transaxial FOV defined as scanner radius / 2
4605  fov_trs = scanner_radius/2;
4606  // 4mm voxels by default
4607  voxels_number_trs = fov_trs/2 + 1;
4608 
4609  uint32_t nb_pixels = number_of_pixels_axl * number_of_pixels_trs;
4610 
4611  pix_size_axl = nb_pixels>1 ? pix_size_axl : crystal_size_axl;
4612  pix_size_trs = nb_pixels>1 ? pix_size_trs : crystal_size_trs;
4613 
4614  // Compute gaps
4615  if (pix_step_axl - pix_size_axl >= 0)
4616  pix_gap_axl = pix_step_axl - pix_size_axl;
4617 
4618  if (pix_step_trs - pix_size_trs >= 0)
4619  pix_gap_trs = pix_step_trs - pix_size_trs;
4620 
4621 
4622  // CASToR x and y axis for rotation inverted in comparison with GATE
4623  head_first_angle = round(atan2f(head_pos_X , head_pos_Y) * 180. / M_PI)
4624  - head_first_angle;
4625 
4626 
4627  // Writing the .geom file
4628  ofstream fileGeom(a_pathGeom.c_str(), ios::out | ios::trunc);
4629 
4630  if(fileGeom)
4631  {
4632  fileGeom << "modality : " << modality << endl;
4633  fileGeom << "scanner name : " << scanner_name << endl;
4634  fileGeom << "number of detector heads : " << number_of_heads << endl;
4635  fileGeom << "trans number of pixels : " << number_of_pixels_trs << endl;
4636  fileGeom << "trans pixel size : " << pix_size_trs << endl;
4637  fileGeom << "trans gap size : " << pix_gap_trs << endl;
4638  fileGeom << "axial number of pixels : " << number_of_pixels_axl << endl;
4639  fileGeom << "axial pixel size : " << pix_size_axl << endl;
4640  fileGeom << "axial gap size : " << pix_gap_axl << endl;
4641 
4642  fileGeom << "detector depth : " << crystal_depth << endl;
4643 
4644  fileGeom << "scanner radius : " << scanner_radius;
4645  for(size_t h=1 ; h<number_of_heads ; h++)
4646  fileGeom << "," << scanner_radius;
4647  fileGeom << endl;
4648 
4649  fileGeom << "# Collimator configuration : "<< endl << endl;
4650  for(size_t h=0 ; h<number_of_heads ; h++)
4651  {
4652  fileGeom << "head" << h+1 << ":" << endl;
4653  fileGeom << "trans focal model: " << focal_model_trs << endl;
4654  fileGeom << "trans number of coef model: " << nb_coeff_model_trs << endl;
4655  fileGeom << "trans parameters: " << coeff_model_trs << endl;
4656  fileGeom << "axial focal model: " << focal_model_axl << endl;
4657  fileGeom << "axial number of coef model: " << nb_coeff_model_axl << endl;
4658  fileGeom << "axial parameters: " << coeff_model_axl << endl;
4659  fileGeom << endl;
4660  }
4661 
4662  fileGeom << "" << endl;
4663  fileGeom << "# default reconstruction parameters" << endl;
4664  fileGeom << "voxels number transaxial : " << voxels_number_trs << endl;
4665  fileGeom << "voxels number axial : " << voxels_number_axl << endl;
4666 
4667  fileGeom << "field of view transaxial : " << fov_trs << endl;
4668  fileGeom << "field of view axial : " << fov_axl << endl << endl ;
4669  fileGeom << ""<< endl;
4670 
4671  fileGeom << "# description" << endl;
4672  fileGeom << "description : " << description << endl;
4673 
4674  fileGeom.close();
4675 
4676  Cout("Output geom file written at :" << a_pathGeom << endl);
4677  }
4678  else
4679  {
4680  Cerr("***** dataConversionUtilities::CreateGeomWithSPECT()-> Couldn't open geom file for writing "<< a_pathGeom << " !" << endl);
4681  return 1;
4682  }
4683 
4684  return 0;
4685 }
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, int vb)
Recover informations about the scanner element of an ECAT system and acquisition duration, from a GATE macro file.
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
#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 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, int vb)
Recover informations about the scanner element of a cylindricalPET system and acquisition duration...
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.
#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 ...