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