CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
src/management/sOutputManager.cc
Go to the documentation of this file.
1 
9 #include "sOutputManager.hh"
10 #include "oImageSpace.hh"
11 #include "oImageDimensionsAndQuantification.hh"
12 #include <iomanip>
13 
14 #ifdef CASToR_USE_CMAKE
15  #include "oCASToRConfig.hh"
16 #endif
17 
18 // =====================================================================
19 // ---------------------------------------------------------------------
20 // ---------------------------------------------------------------------
21 // =====================================================================
22 
23 // Implementation of the exit function that must be used instead of the standard exit
24 void Exit(int code)
25 {
26  if (code!=0) Cerr("***** Exit function called. Abort with code " << code << "." << endl);
27  #ifdef CASTOR_MPI
28  MPI_Finalize();
29  #endif
30  exit(code);
31 }
32 
33 // Singleton : set pointer to object to NULL
35 
36 // =====================================================================
37 // ---------------------------------------------------------------------
38 // ---------------------------------------------------------------------
39 // =====================================================================
40 /*
41  \brief sOutputManager constructor.
42  \details It is private at this class is singleton.
43  It should be instanciated using the GetInstance() function
44  Initialize the member variables to their default values.
45 */
47 {
48  // Initialize all members as default
49  m_verbose = 0;
50  m_mpiRank = 0;
51  m_baseName = "";
52  m_pathName = "";
53  m_pathToConfigDir = "";
55  mp_dataFileName = vector<string>();
56 
57  // Set scientific numbers
58  cout << std::scientific;
59  cerr << std::setprecision(std::numeric_limits<FLTNB>::digits10+1);
60 }
61 
62 
63 
64 // =====================================================================
65 // ---------------------------------------------------------------------
66 // ---------------------------------------------------------------------
67 // =====================================================================
68 /*
69  \brief sOutputManager destructor.
70 */
72 {
73  // Just have to close the log file
74  if (m_mpiRank==0 && m_logFile) m_logFile.close();
75 }
76 
77 
78 
79 // =====================================================================
80 // ---------------------------------------------------------------------
81 // ---------------------------------------------------------------------
82 // =====================================================================
83 /*
84  \fn CheckConfigDir
85  \param a_path
86  \brief Set the path to the CASTOR config directory
87  \details Set the path to the CASTOR config directory from the given path if it is provided
88  Otherwise, try to get it from the environment variable CASTOR_CONFIG
89  \return 0 if success, positive value otherwise
90 */
91 int sOutputManager::CheckConfigDir(const string& a_path)
92 {
93  // Case 1: a path is provided
94  if (a_path!="")
95  {
96  if (m_verbose>=3) Cout("sOutputManager::CheckConfigDir() -> Directory selected from option as '" << a_path << "'" << endl);
97  m_pathToConfigDir = a_path + OS_SEP;
98  }
99  // Case 2: no path provided so we look after the CASTOR_CONFIG environment variable
100  else
101  {
102  #ifdef CASToR_USE_CMAKE
103  string tmp_path = CASTOR_CONFIG;
104  if (tmp_path.empty()) // throw error if empty
105  #elif defined(CASTOR_USE_MINGW)
106  #ifdef CASTOR_CONFIG
107  // This macro CASTOR_CONFIG_STRING is declared in sOutputManager.hh and automatically convert the value from the environment
108  // CASTOR_CONFIG into a string (i.e. including the double quotes so that it can be used here in the affectation)
109  string tmp_path = ConvertAllSlashOccurrencesToBackSlash(CASTOR_CONFIG_STRING);
110  if (tmp_path.empty())
111  #else
112  // Here the CASTOR_CONFIG variable must have been defined before cross-compilation, so we write a message that will make the
113  // compiler crash and display this fake line! Please let it as is.
114  When cross-compiling, you must define the CASTOR_CONFIG environment variable before that. This compilation error you are seeing is normal!
115  #endif
116  #else
117  char* tmp_path = getenv("CASTOR_CONFIG");
118  if (tmp_path==NULL) // throw error if empty
119  #endif
120  {
121  Cerr("***** sOutputManager::CheckConfigDir() -> No path for CASTOR_CONFIG variable provided !" << endl);
122  return 1;
123  }
124  if (m_verbose>=3) Cout("sOutputManager::CheckConfigDir() -> Directory selected from environment variable as '" << ((string)tmp_path) << "'" << endl);
125  m_pathToConfigDir = ((string)tmp_path) + OS_SEP;
126  }
127  // End
128  return 0;
129 }
130 
131 
132 
133 // =====================================================================
134 // ---------------------------------------------------------------------
135 // ---------------------------------------------------------------------
136 // =====================================================================
137 /*
138  \fn GetPathToConfigDir
139  \brief Return the path to the CASTOR config directory
140  \details Just return the path if it has already been initialized
141  Otherwise, the function recovers the path from environnement variables
142  If any error, the working directory is returned instead
143  \return a string containing path to the CASToR configuration directory
144 */
146 {
147  // If the config directory has already been initialized, then simply return its current value
148  if (m_pathToConfigDir!="") return m_pathToConfigDir;
149  // Otherwise, this means that this function is probably called from before the singleton initialization.
150  else
151  {
152  if (m_verbose>=3) Cout("sOutputManager::GetPathToConfigDir ..."<< endl);
153  // We get the directory from the environment variable
154  #if defined(CASToR_USE_CMAKE)
155  string tmp_path = CASTOR_CONFIG;
156  if (tmp_path.empty())
157  #elif defined(CASTOR_USE_MINGW)
158  #ifdef CASTOR_CONFIG
159  // This macro CASTOR_CONFIG_STRING is declared in sOutputManager.hh and automatically convert the value from the environment
160  // CASTOR_CONFIG into a string (i.e. including the double quotes so that it can be used here in the affectation)
161  string tmp_path = ConvertAllSlashOccurrencesToBackSlash(CASTOR_CONFIG_STRING);
162  if (tmp_path.empty())
163  #else
164  // Here the CASTOR_CONFIG variable must have been defined before cross-compilation, so we write a message that will make the
165  // compiler crash and display this fake line! Please let it as is.
166  When cross-compiling, you must define the CASTOR_CONFIG environment variable before that. This compilation error you are seeing is normal!
167  #endif
168  #else
169  char* tmp_path = getenv("CASTOR_CONFIG");
170  if (tmp_path==NULL)
171  #endif
172  {
173  Cerr("***** sOutputManager::CheckConfigDir() -> No path nor CASTOR_CONFIG variable provided ! Try working directory instead." << endl);
174  m_pathToConfigDir = ".";
175  }
176  else
177  #if defined(CASToR_USE_CMAKE) || defined(CASTOR_USE_MINGW)
178  m_pathToConfigDir = tmp_path + OS_SEP;
179  #else
180  m_pathToConfigDir = ((string)tmp_path) + OS_SEP;
181  #endif
182 
183  return m_pathToConfigDir;
184  }
185 }
186 
187 
188 
189 // =====================================================================
190 // ---------------------------------------------------------------------
191 // ---------------------------------------------------------------------
192 // =====================================================================
193 /*
194  \fn InitOutputDirectory
195  \param a_pathFout : path to an output file as provided by the user
196  \param a_pathDout : path to an output directory as provided by the user
197  \brief Create the output directory if any, extract the base name and create the log file.
198  \return 0 if success, and positive value otherwise.
199 */
200 int sOutputManager::InitOutputDirectory(const string& a_pathFout, const string& a_pathDout)
201 {
202  #ifdef CASTOR_VERBOSE
203  if (m_verbose>=4) Cout("+++++ sOutputManager::InitOutputDirectory() -> Enter"<< endl);
204  #endif
205 
206  // Check unicity of the path
207  if (a_pathFout!="" && a_pathDout!="")
208  {
209  Cerr("***** sOutputManager::InitOutputDirectory() -> Either a file path (-fout) or a directory path (dout) should be provided. cannot be both provided, make your choice !" << endl);
210  return 1;
211  }
212 
213  // Check if the provided path ends with '.' or '/' or '\', then alert and crash
214  string the_path = a_pathFout;
215  if (a_pathDout!="") the_path = a_pathDout;
216  string last_char = the_path.substr(the_path.length()-1);
217  if (last_char=="." || last_char==OS_SEP)
218  {
219  Cerr("***** sOutputManager::InitOutputDirectory() -> Please provide a path not finishing by '.', '"<< OS_SEP <<"' character !" << endl);
220  return 1;
221  }
222 
223  // Verbose
224  if (m_verbose>=1) Cout("sOutputManager::InitOutputDirectory() -> Output path is '" << a_pathFout << a_pathDout << "'" << endl);
225 
226  // -------------------------------------------------
227  // First case: a file path is provided
228  // -------------------------------------------------
229 
230  if (a_pathFout!="")
231  {
232  // Get the last slash position
233  size_t last_slash_pos = a_pathFout.find_last_of(OS_SEP);
234  // No slash
235  if (last_slash_pos==string::npos)
236  {
237  m_pathName = "";
238  m_baseName = a_pathFout;
239  }
240  // Some slashes
241  else
242  {
243  // Everything before the slash becomes the path
244  m_pathName = a_pathFout.substr(0,last_slash_pos+1);
245  // Everything after the slash becomes the base name
246  m_baseName = a_pathFout.substr(last_slash_pos+1);
247  }
248  }
249 
250  // -------------------------------------------------
251  // Second case: a directory path is provided
252  // -------------------------------------------------
253 
254  else if (a_pathDout!="")
255  {
256  // Get the last slash position
257  size_t last_slash_pos = a_pathDout.find_last_of(OS_SEP);
258  // No slash
259  if (last_slash_pos==string::npos)
260  {
261  m_pathName = a_pathDout + OS_SEP;
262  m_baseName = a_pathDout;
263  }
264  // Some slashes
265  else
266  {
267  // The whole becomes the path
268  m_pathName = a_pathDout + OS_SEP;
269  // Everything after the slash becomes the base name
270  m_baseName = a_pathDout.substr(last_slash_pos+1);
271  }
272  // Create directory only for first MPI instance
273  if (m_mpiRank==0)
274  {
275  #ifdef _WIN32
276  string instruction = "if not exist " + m_pathName + " mkdir " + m_pathName;
277  #else
278  string instruction = "mkdir -p " + m_pathName;
279  #endif
280  int error = system(instruction.c_str());
281  if (error)
282  {
283  Cerr("***** sOutputManager::InitOutputDirectory() -> Failed to create output directory with name '" << m_pathName << "' !" << endl);
284  return 1;
285  }
286  }
287  // Verbose
288  if (m_verbose>=3) Cout(" --> Output files will be written inside directory '" << m_pathName << "'" << endl);
289  }
290 
291  // -------------------------------------------------
292  // Third: create the log file
293  // -------------------------------------------------
294 
295  // Only first MPI instance deals with this
296  if (m_mpiRank==0)
297  {
298  // Create file name
299  string log_file_name = m_pathName + m_baseName + ".log";
300  // Open and check
301  m_logFile.open(log_file_name.c_str());
302  if (!m_logFile)
303  {
304  Cerr("***** sOutputManager::InitOutputDirectory() -> Failed to create output log file as '" << log_file_name << "' ! Are you sure the provided output path exists ?" << endl);
305  return 1;
306  }
307  }
308 
309  // End
310  return 0;
311 }
312 
313 // =====================================================================
314 // ---------------------------------------------------------------------
315 // ---------------------------------------------------------------------
316 // =====================================================================
317 
318 int sOutputManager::LogCommandLine(int argc, char** argv)
319 {
320  if (m_verbose>=3) Cout("sOutputManager::LogCommandLine() -> Write command line into the log file"<< endl);
321 
322  // Exit function is MPI rank is anything other than 0
323  if (m_mpiRank!=0) return 0;
324 
325  if (m_logFile)
326  {
327  m_logFile << "==================================================================================================" << endl;
328  m_logFile << " COMMAND LINE CONTEXT" << endl;
329  m_logFile << "==================================================================================================" << endl;
330  // Print command line
331  m_logFile << "Command line: ";
332  for (int i=0; i<argc; i++) m_logFile << argv[i] << " ";
333  m_logFile << endl;
334  #ifdef _WIN32
335  char pwd[MAX_PATH];
336  GetCurrentDirectory(MAX_PATH,pwd);
337  m_logFile << "Working directory: " << pwd << endl;
338  #else
339  m_logFile << "Working directory: " << getenv("PWD") << endl;
340  #endif
341  std::time_t date_of_execution = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
342  m_logFile << "Data of execution: " << std::ctime(&date_of_execution);
343  m_logFile << "Float numbers precision in bytes (for matrices and some computations): " << sizeof(FLTNB) << endl;
344  m_logFile << "Float numbers precision in bytes (for sensitive computations that require at least double precision): " << sizeof(HPFLTNB) << endl;
345  m_logFile << "Float numbers precision in bytes (for datafile reading/writing): " << sizeof(FLTNBDATA) << endl;
346  m_logFile << "Float numbers precision in bytes (for scanner LUT reading/writing): " << sizeof(FLTNBLUT) << endl;
347  m_logFile << "CASToR version: " << CASTOR_VERSION << endl;
348  m_logFile << "==================================================================================================" << endl << flush;
349  }
350  else
351  return 1;
352 
353  // End
354  return 0;
355 }
356 
357 
358 
359 
360 // =====================================================================
361 // ---------------------------------------------------------------------
362 // ---------------------------------------------------------------------
363 // =====================================================================
364 /*
365  \fn inline int sOutputManager::SetOutNbPrec()
366  \param a_format : string containing output format and precision
367  \brief Set the output format and precision used for numeric display
368  \return 0 if success, positive value otherwise
369 */
370 int sOutputManager::SetOutNbPrec(string a_format)
371 {
372  string format;
373  int32_t precision;
374  string option = "-onbc";
375 
376  // parsing
377  size_t pos = a_format.find_first_of(",");
378  if (pos == string::npos)
379  {
380  Cerr("***** sOutputManager::SetOutNbPrec() -> Error, format must be (format,precision) (e.g = (f,5) or (s,0)!" << endl);
381  return 1;
382  }
383 
384  format = a_format.substr(0,pos);
385  if (format == "f")
386  {
387  cout << std::fixed;
388  if (m_logFile) m_logFile << std::fixed;
389  }
390  else
391  {
392  cout << std::scientific;
393  if (m_logFile) m_logFile << std::scientific;
394  }
395 
396  if (ReadStringOption(a_format.substr(pos+1), &precision, 1, ",", option))
397  {
398  Cerr("***** sOutputManager::SetOutNbPrec() -> Invalid argument " << a_format << " for option " << option << " !" << endl);
399  Cerr("***** sOutputManager::SetOutNbPrec() -> Format must be (format,precision) (e.g = (f,5) or (s,0)!" << endl);
400  return 1;
401  }
402 
403  if (precision>0)
404  {
405  cout << std::setprecision(precision);
406  if (m_logFile) m_logFile << std::setprecision(precision);
407  }
408  else
409  {
410  cout << std::setprecision(std::numeric_limits<FLTNB>::digits10+1);
411  if (m_logFile) m_logFile << std::setprecision(std::numeric_limits<FLTNB>::digits10+1);
412  }
413  return 0;
414 }
415 
416 // =====================================================================
417 // ---------------------------------------------------------------------
418 // ---------------------------------------------------------------------
419 // =====================================================================
string ConvertAllSlashOccurrencesToBackSlash(const string &a_path)
int ReadStringOption(const string &a_input, T *ap_return, int a_nbElts, const string &sep, const string &a_option)
Parse the &#39;a_input&#39; string corresponding to the &#39;a_option&#39; into &#39;a_nbElts&#39; elements, using the &#39;sep&#39; separator. The results are returned in the templated &#39;ap_return&#39; dynamic templated array. Call "ConvertFromString()" to perform the correct conversion depending on the type of the data to convert.
sOutputManager()
sOutputManager constructor.
#define Cerr(MESSAGE)
int LogCommandLine(int argc, char **argv)
int CheckConfigDir(const string &a_path)
Singleton class that manages output writing on disk (images, sinograms, etc). It also manages loggi...
const string & GetPathToConfigDir()
Return the path to the CASTOR config directory.
#define CASTOR_CONFIG
Definition: oCASToRConfig.hh:4
void Exit(int code)
int InitOutputDirectory(const string &a_pathFout, const string &a_pathDout)
~sOutputManager()
sOutputManager destructor.
#define Cout(MESSAGE)