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