cloudy  trunk
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
cpu.cpp
Go to the documentation of this file.
1 /* This file is part of Cloudy and is copyright (C)1978-2017 by Gary J. Ferland and
2  * others. For conditions of distribution and use see copyright notice in license.txt */
5 #include "cdstd.h"
6 
7 #if defined(__ia64) && defined(__INTEL_COMPILER)
8 extern "C" unsigned long fpgetmask();
9 extern "C" void fpsetmask(unsigned long);
10 #endif
11 
12 #if defined(__sun) || defined(__sgi)
13 #include <ieeefp.h>
14 #if defined(HAVE_SUNMATH) || defined(FLUSH_DENORM_TO_ZERO)
15 #include <sunmath.h>
16 #endif
17 #endif
18 
19 #if defined(__alpha) && defined(__linux__) && defined(__GNUC__)
20 #define __USE_GNU
21 #include <fenv.h>
22 #endif
23 
24 #if defined(__unix) || defined(__APPLE__)
25 #include <unistd.h>
26 #endif
27 
28 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
29 #include <sys/types.h>
30 #include <sys/sysctl.h>
31 #endif
32 
33 /* the redefinition of float in cddefines.h can cause problems in system headers
34  * hence these includes MUST come after the system header includes above */
35 #include "cddefines.h"
36 #include "path.h"
37 #include "trace.h"
38 #include "service.h"
39 #include "thirdparty.h"
40 #include "version.h"
41 
42 STATIC NORETURN void AbortErrorMessage( const char* fname, const vector<string>& PathList, access_scheme scheme );
43 STATIC string check_mult_path( const char* fname, const vector<string>& PathList, access_scheme scheme, bool lgRead );
44 
45 // Use Schwartz/nifty counter to ensure that global policy class
46 // is set up before other globals/statics, and deleted last.
48 static int cpu_count = 0;
50 {
51  if (0 == cpu_count++)
52  {
53  m_i = new t_cpu_i;
54  }
55 }
57 {
58  if (0 == --cpu_count)
59  {
60  delete m_i;
61  }
62 }
63 
64 /* NB NB - this constructor needs to be called before any of the user code is executed !! */
66 {
67  DEBUG_ENTRY( "t_cpu_i()" );
68 
69  // set up signal handlers so that we can control what happens...
71 
72  p_exit_status.resize( ES_TOP, "--undefined--" );
73  p_exit_status[ES_SUCCESS] = "ok";
74  p_exit_status[ES_FAILURE] = "early termination";
75  p_exit_status[ES_WARNINGS] = "warnings";
76  p_exit_status[ES_BOTCHES] = "botched monitors";
77  p_exit_status[ES_CLOUDY_ABORT] = "cloudy abort";
78  p_exit_status[ES_BAD_ASSERT] = "failed assert";
79  p_exit_status[ES_BAD_ALLOC] = "failed memory alloc";
80  p_exit_status[ES_OUT_OF_RANGE] = "array bound exceeded";
81  p_exit_status[ES_DOMAIN_ERROR] = "math domain error";
82  p_exit_status[ES_USER_INTERRUPT] = "user interrupt";
83  p_exit_status[ES_TERMINATION_REQUEST] = "process killed";
84  p_exit_status[ES_ILLEGAL_INSTRUCTION] = "illegal instruction";
85  p_exit_status[ES_FP_EXCEPTION] = "fp exception";
86  p_exit_status[ES_SEGFAULT] = "segmentation fault";
87  p_exit_status[ES_BUS_ERROR] = "bus error";
88  p_exit_status[ES_UNKNOWN_SIGNAL] = "unknown signal";
89  p_exit_status[ES_UNKNOWN_EXCEPTION] = "unknown exception";
90 
91  /* >>chng 05 dec 14, add test of endianness of the CPU, PvH */
92  endian.c[0] = 0x12;
93  endian.c[1] = 0x34;
94  endian.c[2] = 0x56;
95  endian.c[3] = 0x78;
96 
97  /* >>chng 05 dec 15, add signaling NaN for float and double to cpu struct, PvH */
98  /* in C++ this should be replaced by numeric_limits<TYPE>::signaling_NaN() */
99  if( sizeof(sys_float) == 4 )
100  {
101 # ifdef __mips
102  /* definition of signaling and quiet NaN is reversed on MIPS */
103  Float_SNaN_Value = 0xffffffff;
104 # else
105  if( big_endian() || little_endian() )
106  {
107  /* this should work on most modern CPU's */
108  Float_SNaN_Value = 0xffbfffff;
109  }
110  else
111  {
112  /* this is an unusual CPU -> bit pattern for SNaN is unknown */
113  Float_SNaN_Value = -1;
114  }
115 # endif
116  }
117  else
118  {
119  /* this is an unusual CPU -> bit pattern for SNaN is unknown */
120  Float_SNaN_Value = -1;
121  }
122 
123 # ifdef HAVE_INT64
124 
125  if( sizeof(double) == 8 )
126  {
127 # ifdef __mips
128  /* definition of signaling and quiet NaN is reversed on MIPS */
129  Double_SNaN_Value = INT64_LIT(0xffffffffffffffff);
130 # else
131  /* this should work on most modern CPU's */
132  Double_SNaN_Value = INT64_LIT(0xfff7ffffffbfffff);
133 # endif
134  }
135  else
136  {
137  /* this is an unusual CPU -> bit pattern for SNaN is unknown */
138  Double_SNaN_Value = -1;
139  }
140 
141 # else
142 
143  if( sizeof(double) == 8 )
144  {
145 # ifdef __mips
146  /* definition of signaling and quiet NaN is reversed on MIPS */
147  Double_SNaN_Value[0] = 0xffffffff;
148  Double_SNaN_Value[1] = 0xffffffff;
149 # else
150  if( big_endian() )
151  {
152  /* this should work on most modern CPU's */
153  Double_SNaN_Value[0] = 0xfff7ffff;
154  Double_SNaN_Value[1] = 0xffbfffff;
155  }
156  else if( little_endian() )
157  {
158  /* this should work on most modern CPU's */
159  Double_SNaN_Value[0] = 0xffbfffff;
160  Double_SNaN_Value[1] = 0xfff7ffff;
161  }
162  else
163  {
164  /* this is an unusual CPU -> bit pattern for SNaN is unknown */
165  Double_SNaN_Value[0] = -1;
166  Double_SNaN_Value[1] = -1;
167  }
168 # endif
169  }
170  else
171  {
172  /* this is an unusual CPU -> bit pattern for SNaN is unknown */
173  Double_SNaN_Value[0] = -1;
174  Double_SNaN_Value[1] = -1;
175  }
176 
177 # endif
178 
179  /* set FP environment to trap FP exceptions */
180  enable_traps();
181 
182  ioStdin = stdin;
183  ioQQQ = stdout;
184  ioPrnErr = stderr;
185  lgPrnErr = false;
186 
187  test_float = FLT_MIN;
188  test_double = DBL_MIN;
189 
190  /* default is for failed asserts not to abort */
191  p_lgAssertAbort = false;
192 
193  const char *str;
194 
195  /* determine the no. of CPUs on this machine; used by PHYMIR, grid command, .... */
196 # if defined(_SC_NPROCESSORS_ONLN) /* Linux, Sun Sparc, DEC Alpha, MacOS (OS releases >= 10.4) */
197 #if defined(__APPLE__) /* MacOS only use physical cores*/
198  size_t sizeOfInt = sizeof(int);
199  int physicalCores;
200  sysctlbyname("hw.physicalcpu", &physicalCores, &sizeOfInt, NULL, 0);
201  n_avail_CPU = int(physicalCores);
202 #else
203  n_avail_CPU = sysconf(_SC_NPROCESSORS_ONLN);
204 #endif
205 # elif defined(_SC_NPROC_ONLN) /* SGI Iris */
206  n_avail_CPU = sysconf(_SC_NPROC_ONLN);
207 # elif defined(_SC_CRAY_NCPU) /* Cray */
208  n_avail_CPU = sysconf(_SC_CRAY_NCPU);
209 # elif defined(_WIN32) /* Microsoft Windows */
210  str = getenv( "NUMBER_OF_PROCESSORS" );
211  if( str != NULL )
212  {
213  int found = sscanf( str, "%ld", &n_avail_CPU );
214  if( found != 1 )
215  n_avail_CPU = 1;
216  }
217  else
218  {
219  n_avail_CPU = 1;
220  }
221 # elif defined(HW_AVAILCPU) /* MacOS, BSD variants */
222 #if defined(__APPLE__) /* MacOS only use physical cores*/
223  size_t sizeOfInt = sizeof(int);
224  int physicalCores;
225  sysctlbyname("hw.physicalcpu", &physicalCores, &sizeOfInt, NULL, 0);
226  n_avail_CPU = int(physicalCores);
227 #else
228  int mib[2];
229  size_t len = sizeof(n_avail_CPU);
230  mib[0] = CTL_HW;
231  mib[1] = HW_AVAILCPU; // alternatively, try HW_NCPU;
232  sysctl(mib, 2, &n_avail_CPU, &len, NULL, 0);
233  if( n_avail_CPU < 1 )
234  {
235  mib[1] = HW_NCPU;
236  sysctl(mib, 2, &n_avail_CPU, &len, NULL, 0);
237  if( n_avail_CPU < 1 )
238  n_avail_CPU = 1;
239  }
240 #endif
241 # else
242  /* Other systems, supply no. of CPUs on OPTIMIZE PHYMIR command line */
243  n_avail_CPU = 1;
244 # endif
245  /* the constructor is run before MPI starts, so the rank is not available yet */
246 # ifdef MPI_ENABLED
247  p_lgMPI = true;
248 # else
249  p_lgMPI = false;
250 # endif
251  /* the default is for all ranks to cooperate on the same sim */
252  p_lgMPISingleRankMode = false;
253  n_rank = 0;
254 
255 # ifdef _WIN32
256  str = getenv( "COMPUTERNAME" );
257 # else
258  str = getenv( "HOSTNAME" );
259 # endif
260 
261  if( str != NULL )
262  strncpy( HostName, str, STDLEN );
263  else
264  strncpy( HostName, "unknown", STDLEN );
265  HostName[STDLEN-1] = '\0';
266 
267  /* pick up the path from the environment, if set by user */
268  const char *path = getenv( "CLOUDY_DATA_PATH" );
269 
270 # ifdef _WIN32
271  string separator( ";" );
272  p_chDirSeparator = '\\';
273 # else
274  string separator( ":" );
275  p_chDirSeparator = '/';
276 # endif
277 
278  // if the environment variable was not set, use the preprocessor symbol CLOUDY_DATA_PATH
279  // this is normally defined in the Makefile, but can also be set in path.h (deprecated)
280  string chSearchPathRaw = ( path != NULL ) ? string( path ) : string( CLOUDY_DATA_PATH );
281  // the current working directory should be first
282  chSearchPathRaw = "." + separator + chSearchPathRaw + separator;
283 
284 #ifdef CLOUDY_ROOT
285  // CLOUDY_ROOT is only defined in the Makefile
286  string chCloudyRoot = string( CLOUDY_ROOT );
287 
288  // expand "+" to the default search path for this installation
289  while( FindAndReplace( chSearchPathRaw, separator + "+" + separator,
290  separator + chCloudyRoot + p_chDirSeparator + "data" + separator ) ) {}
291 #endif
292 
293  Split( chSearchPathRaw, separator, chSearchPath, SPM_RELAX );
294 
295  for( vector<string>::iterator p=chSearchPath.begin(); p != chSearchPath.end(); ++p )
296  {
297 #ifdef HAVE_REALPATH
298  // clean up path
299  char* ptr = realpath( p->c_str(), NULL );
300  if( ptr != NULL )
301  {
302  *p = ptr;
303  free( ptr );
304  }
305 #endif
306 
307  /* get last valid char */
308  char chEnd = *p->rbegin();
309 
310  /* make sure path ends with directory separator */
311  if( chEnd != p_chDirSeparator )
312  *p += p_chDirSeparator;
313 
314 #if 0
315  // enable this code block once the search path is implemented in its final form
316  // test it by running a sim inside the data directory that e.g. uses grains
317  // it should not produce any warnings about multiple grain files along the path
318 
319  // check if this path component is already present
320  if( find( chSearchPath.begin(), p, *p ) != p )
321  chSearchPath.erase(p);
322 #endif
323  }
324 
325  nFileDone = 0;
326 
327  getMD5sums( "md5datafiles.dat" );
328 }
329 
331 {
332  /* >>chng 01 aug 07, added code to circumvent math library bug with g++ on
333  * alpha-linux machines, see bug report 51072 on http://bugzilla.redhat.com, PvH */
334  /* >>chng 01 apr 17, added code for Solaris and SGI operating systems, PvH */
335  /* this routine contains no code for alphas or crays, they do not need
336  * special code to enable FP exceptions since they are enabled by default */
337 
338  /* there is no command line option on MS Visual Studio to force crash */
339 # if defined(_MSC_VER)
340  volatile unsigned int NewMask;
341 
342  /* | is a bitwise inclusive or, turns on bits
343  * 0|0 = 0
344  * 0|1 = 1|0 = 1|1 = 1 */
345  NewMask = _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_INVALID;
346  /* ~ is the unary bitwise complement - all bits flip */
347  NewMask = ~NewMask;
348  _controlfp( NewMask , _MCW_EM );
349 
350  /* this is the code for Linux PC (but not Linux alpha) to force crash */
351  /* >>chng 04 apr 26, added support for AMD64, enable FPE traps for SSE/SSE2, PvH */
352  /* >>chng 06 aug 12, added support for Apple MacOSX, and hopefully also Solaris x86, PvH */
353  // do not enable trapping FPEs for Clang compiler since it is not supported in v3.4 and later
354 # elif defined(__GNUC__) && ( defined(__i386) || defined(__amd64) ) && !defined(__clang__)
355  volatile unsigned int Old_Mask, New_Mask;
356 # if defined(__SSE__) || defined(__SSE2__)
357  volatile unsigned int SSE_Mask;
358 # endif
359 
360 # define _FPU_MASK_IM 0x01 /* Invalid */
361 # define _FPU_MASK_DM 0x02 /* Denormalized */
362 # define _FPU_MASK_ZM 0x04 /* Division-by-zero */
363 # define _FPU_MASK_OM 0x08 /* Overflow */
364 # define _FPU_MASK_UM 0x10 /* Underflow */
365 # define _FPU_MASK_PM 0x20 /* Inexact */
366 
367  /* | is a bitwise inclusive or, turns on bits */
368  /* 0|0 = 0 */
369  /* 0|1 = 1|0 = 1|1 = 1 */
370 
371  /* ~ is the unary bitwise complement - all bits flip */
372 
373  /* this enables FPE traps for regular i387 FP instructions */
374 
375  volatile unsigned int UnMask = ~((unsigned int)( _FPU_MASK_ZM | _FPU_MASK_IM | _FPU_MASK_OM ));
376 
377  __asm__ volatile("fnstcw %0" : "=m" (*&Old_Mask));
378 
379  New_Mask = Old_Mask & UnMask;
380 
381  __asm__ volatile("fldcw %0" : : "m" (*&New_Mask));
382 
383 # if defined(__SSE__) || defined(__SSE2__)
384 
385 # ifndef USE_DENORM
386  /* using this causes denormalized numbers to be flushed to zero,
387  * which will speed up the code on Pentium 4 processors */
388  SSE_Mask = 0x9900;
389 # else
390  /* this version allows denormalized numbers to be retained */
391  SSE_Mask = 0x1900;
392 # endif
393 
394  /* this enables FPE traps for SSE/SSE2 instructions */
395 
396  __asm__ volatile( "ldmxcsr %0" : : "m" (*&SSE_Mask) );
397 
398 # endif
399 
400  /* this is for IA64 systems running g++ or icc (e.g. SGI, HP, ...) */
401 # elif defined(__ia64)
402 
403 # define FPSR_TRAP_VD (1 << 0) /* invalid op trap disabled */
404 # define FPSR_TRAP_DD (1 << 1) /* denormal trap disabled */
405 # define FPSR_TRAP_ZD (1 << 2) /* zero-divide trap disabled */
406 # define FPSR_TRAP_OD (1 << 3) /* overflow trap disabled */
407 # define FPSR_TRAP_UD (1 << 4) /* underflow trap disabled */
408 # define FPSR_TRAP_ID (1 << 5) /* inexact trap disabled */
409 
410 # define FPSR_SF0_FTZ (1 << 6) /* flush denormalized numbers to zero */
411 
412 # if defined(__GNUC_EXCL__)
413  /* __asm__ instructions are not supported by icc as of v9.0 */
414 # define _IA64_REG_AR_FPSR 40
415 
416 # define ia64_getreg( regnum ) __asm__ volatile( "mov %0=ar%1" : "=r" (fpsr) : "i"(regnum) )
417 # define ia64_setreg( regnum, val ) __asm__ volatile( "mov ar%0=%1" :: "i" (regnum), "r"(val): "memory" )
418 # define ia64_serialize __asm__ volatile( "srlz.i" );
419 
420  volatile unsigned long fpsr, flags = FPSR_TRAP_VD | FPSR_TRAP_ZD | FPSR_TRAP_OD;
421 
422  ia64_getreg( _IA64_REG_AR_FPSR );
423  fpsr &= ~flags;
424 # if defined(FLUSH_DENORM_TO_ZERO)
425  fpsr |= FPSR_SF0_FTZ;
426 # endif
427  ia64_setreg( _IA64_REG_AR_FPSR, fpsr );
428  /* this prevents RAW and WAW dependency violations in case this ever gets inlined... */
429  ia64_serialize;
430 
431 # elif defined(__INTEL_COMPILER)
432  /* this is for icc on IA64 SGI machines */
433  unsigned long fpsr = fpgetmask();
434  fpsr |= FPSR_TRAP_VD | FPSR_TRAP_ZD | FPSR_TRAP_OD;
435  fpsetmask( fpsr );
436 # endif /* defined(__GNUC_EXCL__) */
437 
438  /* this is for Solaris and SGI to force crash */
439 # elif defined(__sun) || defined(__sgi)
440 
441  fp_except mask;
442 
443  /* >>chng 05 dec 30, accept FLUSH_DENORM_TO_ZERO as a synonym for HAVE_SUNMATH, PvH */
444 # if defined(HAVE_SUNMATH) || defined(FLUSH_DENORM_TO_ZERO)
445 
446  /* >>chng 01 oct 09, disable gradual underflow on ultrasparc whith g++
447  * (only needed for versions < 3.1 or >= 4.3.0, see Note 1).
448  *
449  * compile this module with:
450  * g++ [ other options... ] -I<include-dir> -DHAVE_SUNMATH -c cpu.cpp
451  * link the program with:
452  * g++ -L<library-dir> -o cloudy.exe *.o -lsunmath
453  *
454  * you probably need to use -I<include-dir> and -L<library-dir> to point the
455  * compiler/linker to the location of the sunmath.h header file and libsunmath.so
456  * library (e.g., -I/opt/SUNWspro/prod/include/cc -L/opt/SUNWspro/lib; note that
457  * the actual location may vary from one installation to another).
458  * See also bug report 4487 on http://gcc.gnu.org/bugzilla/
459  *
460  * Note 1: Starting with g++ 3.1, bug 4487 has been solved: -funsafe-math-optimizations
461  * will automatically disable gradual underflow. Hence using nonstandard_arithmetic()
462  * is no longer necessary. The option -funsafe-math-optimizations should be included
463  * both when compiling and linking:
464  *
465  * g++ [ other options... ] -funsafe-math-optimizations -c *.c
466  * g++ [ other options... ] -funsafe-math-optimizations -o cloudy.exe *.o
467  *
468  * Starting with g++ 4.3.0 the -funsafe-math-optimizations option can no longer be
469  * used as it implicitly enables -fno-trapping-math, which is unsafe for Cloudy
470  * because we do trap floating point exceptions.
471  *
472  * Note 2: Don't use nonstandard_arithmetic() with CC (the SunWorks/Forte compiler);
473  * use the -fast commandline option instead to disable gradual underflow (or use
474  * -fnonstd if you don't want all the other options enabled by -fast). The option
475  * -fast (or -fnonstd) should be included both when compiling and linking:
476  *
477  * CC [ other options... ] -fast -c *.c
478  * CC -fast -o cloudy.exe *.o
479  *
480  * PvH */
481  nonstandard_arithmetic();
482 # endif
483 
484  /* enable floating point exceptions on sun and sgi */
485  mask = fpgetmask();
486  mask = mask | FP_X_INV | FP_X_OFL | FP_X_DZ;
487  fpsetmask(mask);
488 
489 # elif defined(__alpha) && defined(__linux__) && defined(__GNUC__)
490 
491  /* the following is not supported on all hardware platforms, but certainly for EV56
492  * and later. earlier versions may work as well, but that has not been tested.
493  * for details see https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=51072 */
494 # ifdef FE_NONIEEE_ENV
495  /* this prevents the infamous math library bug when compiling with gcc on alpha-linux
496  * machines. if this doesn't work on your system, the only alternative is to link
497  * against the Compaq math library: gcc *.o -lcpml -lm, or use ccc itself, PvH */
498  fesetenv(FE_NONIEEE_ENV);
499 # endif
500 
501 # endif
502  return;
503 }
504 
506 {
507  DEBUG_ENTRY( "set_signal_handlers()" );
508 
509 #ifdef CATCH_SIGNAL
510 # ifdef __unix
511  p_action.sa_handler = &signal_handler;
512  sigemptyset( &p_action.sa_mask );
513  p_action.sa_flags = SA_NODEFER;
514 
515  p_default.sa_handler = SIG_DFL;
516  sigemptyset( &p_default.sa_mask );
517  p_default.sa_flags = SA_NODEFER;
518 
519  for( int sig=1; sig <= 31; sig++ )
520  {
521  // is the signal valid?
522  if( sigaction( sig, NULL, NULL ) == 0 )
523  // these two are for suspending and resuming a job
524  if( sig != SIGSTOP && sig != SIGCONT )
525  sigaction( sig, action(), NULL );
526  }
527 # endif
528 
529 # ifdef _MSC_VER
530  signal( SIGABRT, &signal_handler );
531  signal( SIGFPE, &signal_handler );
532  signal( SIGILL, &signal_handler );
533  signal( SIGINT, &signal_handler );
534  signal( SIGSEGV, &signal_handler );
535  signal( SIGTERM, &signal_handler );
536 # endif
537 #endif
538 }
539 
541 {
542  // when an FPE is caught, the mask is reset...
543  cpu.i().enable_traps();
544 # ifdef _MSC_VER
545  // at this point the signal handler has reverted to the default handler
546  signal( sig, &signal_handler );
547 # endif
548  throw bad_signal( sig );
549 }
550 
551 
553 {
554  fprintf(ioQQQ, "The path is:\n");
555  for( vector<string>::size_type i=1; i < chSearchPath.size(); ++i )
556  fprintf( ioQQQ, " ==%s==\n", chSearchPath[i].c_str() );
557 }
558 
559 // this routine generates a list of all full paths to the locations where we should look for the file
560 void t_cpu_i::getPathList( const char* fname, vector<string>& PathList, access_scheme scheme, bool lgRead ) const
561 {
562  DEBUG_ENTRY( "getPathList()" );
563 
564  vector<string>::size_type begin, end;
565 
566  switch( scheme )
567  {
568  case AS_DATA_ONLY:
569  case AS_DATA_ONLY_TRY:
570  case AS_DATA_OPTIONAL:
571  begin = 1;
572  end = chSearchPath.size();
573  break;
574  case AS_LOCAL_DATA:
575  case AS_LOCAL_DATA_TRY:
576  begin = 0;
577  end = chSearchPath.size();
578  break;
579  case AS_LOCAL_ONLY:
580  case AS_LOCAL_ONLY_TRY:
581  case AS_SILENT_TRY:
582  begin = 0;
583  end = 1;
584  break;
585  case AS_DEFAULT:
586  ASSERT( !lgRead );
587  // these values are not used, but need to be there
588  // to avoid warnings about using uninitialized vars
589  begin = 0;
590  end = 1;
591  break;
592  default:
593  TotalInsanity();
594  }
595 
596  PathList.clear();
597  string FileName( fname );
598  if( lgRead )
599  {
600  for( vector<string>::size_type i=begin; i < end; ++i )
601  PathList.push_back( chSearchPath[i] + FileName );
602  }
603  else
604  {
605  PathList.push_back( FileName );
606  }
607 }
608 
609 void t_cpu_i::getMD5sums( const char* fname )
610 {
611  DEBUG_ENTRY( "getMD5sums()" );
612 
613  // this routine reads a file with expected md5sum values for all Cloudy data files
614  // they will be stored in the map md5sum_expct[] and can be used to compare to the
615  // actual md5sum of the data files that were read in.
616 
617  md5sum_expct.clear();
618  // we cannot use open_data() here, likely because the t_cpu_i c'tor hasn't completed yet
619  // getPathList() would get a NULL this-pointer in that case and crash on a segfault...
620  // so we have to copy a skeleton version of the open_data() code here...
621  vector<string> PathList;
622  getPathList( fname, PathList, AS_DATA_ONLY_TRY, true );
623  fstream io;
624  vector<string>::const_iterator ptr;
625  for( ptr=PathList.begin(); ptr != PathList.end() && !io.is_open(); ++ptr )
626  io.open( ptr->c_str(), mode_r );
627  // if the file is not found, we return quietly
628  if( !io.is_open() )
629  return;
630  ++nFileDone;
631  // the file has been opened, now parse the contents
632  string line;
633  char dirSep = '#';
634  while( getline( io, line ) )
635  {
636  // determine what directory separator is used in the file
637  if( dirSep == '#' )
638  {
639  if( line.find( '/' ) != string::npos )
640  dirSep = '/';
641  if( line.find( '\\' ) != string::npos )
642  dirSep = '\\';
643  }
644  // replace the directory separator if the local OS uses a different one
645  if( dirSep != '#' && dirSep != p_chDirSeparator )
646  while( FindAndReplace( line, string(1,dirSep), string(1,p_chDirSeparator) ) ) {}
647  string md5sum, path;
648  istringstream iss( line );
649  iss >> md5sum >> path;
650  md5sum_expct[path] = md5sum;
651  }
652 }
653 
654 STATIC NORETURN void AbortErrorMessage( const char* fname, const vector<string>& PathList, access_scheme scheme )
655 {
656  DEBUG_ENTRY( "AbortErrorMessage()" );
657 
658  if( scheme == AS_DATA_OPTIONAL )
659  // presence is optional -> make warning less scary...
660  fprintf( ioQQQ, "\nI could not open the data file %s\n\n", fname );
661  else
662  fprintf( ioQQQ, "\nPROBLEM DISASTER I could not open the data file %s\n\n", fname );
663  if( cpu.i().firstOpen() || scheme == AS_DATA_ONLY )
664  {
665  // failed on very first open -> most likely path is not correct
666  // failed on AS_DATA_ONLY -> CLOUDY_DATA_PATH may point to obsolete data dir
667  fprintf( ioQQQ, "Although there may be other reasons you have received this error,\n");
668  fprintf( ioQQQ, "the most likely are that the path has not been properly set or\n");
669  fprintf( ioQQQ, "that the path points to an old version of the data. It should\n");
670  fprintf( ioQQQ, "point to the data directory you downloaded from the web site.\n\n");
671  fprintf( ioQQQ, "Please use \"make\" to compile the code. This will automatically\n");
672  fprintf( ioQQQ, "set the path correctly. Alternatively you can set the environment\n");
673  fprintf( ioQQQ, "variable CLOUDY_DATA_PATH to point to the data directory using\n");
674  fprintf( ioQQQ, "the shell command \nexport CLOUDY_DATA_PATH=\"/path/to/data\"\n");
675  fprintf( ioQQQ, "from a bash command prompt.\n\n");
676  cpu.i().printDataPath();
677  }
678  else
679  {
680  // failed on search including local directory -> most likely the file name
681  // was mistyped on a compile command, or Cloudy is run in the wrong directory
682  // if scheme == AS_DATA_OPTIONAL, this most likely is a stellar grid that is not installed.
683  fprintf( ioQQQ, "These are all the paths I tried:\n" );
684  for( vector<string>::const_iterator ptr=PathList.begin(); ptr != PathList.end(); ++ptr )
685  fprintf( ioQQQ, " ==%s==\n", ptr->c_str() );
686  // AS_DATA_OPTIONAL files should provide their own message (currently only stellar grids)
687  if( scheme != AS_DATA_OPTIONAL )
688  {
689  fprintf( ioQQQ, "\nAlthough there may be other reasons you have received this error,\n");
690  fprintf( ioQQQ, "the most likely are that you mistyped the file name, or that you\n");
691  fprintf( ioQQQ, "are running Cloudy in the wrong directory. If you are running a\n");
692  fprintf( ioQQQ, "COMPILE command, this needs to be done in the data directory.\n\n");
693  fprintf( ioQQQ, "It is is also possible that the path has not been properly set. It\n");
694  fprintf( ioQQQ, "should point to the data directory you downloaded from the web site.\n");
695  fprintf( ioQQQ, "Please use \"make\" to compile the code. This will automatically\n");
696  fprintf( ioQQQ, "set the path correctly. Alternatively you can set the environment\n");
697  fprintf( ioQQQ, "variable CLOUDY_DATA_PATH to point to the data directory using\n");
698  fprintf( ioQQQ, "the shell command \nexport CLOUDY_DATA_PATH=\"/path/to/data\"\n");
699  fprintf( ioQQQ, "from a bash command prompt.\n\n");
700  }
701  }
702  fprintf(ioQQQ, "Sorry.\n\n\n");
704 }
705 
706 STATIC string check_mult_path( const char* fname, const vector<string>& PathList, access_scheme scheme, bool lgRead )
707 {
708  DEBUG_ENTRY( "check_mult_path()" );
709 
710  if( !lgRead )
711  {
712  ASSERT( PathList.size() == 1 );
713  if( trace.lgTrace && scheme != AS_SILENT_TRY )
714  fprintf( ioQQQ, " open_data writing %s\n", PathList[0].c_str() );
715  return PathList[0];
716  }
717 
718  vector<string>::const_iterator ptr;
719  vector<string> PathSuccess;
720  for( ptr=PathList.begin(); ptr != PathList.end(); ++ptr )
721  {
722  FILE* handle = sys_fopen( ptr->c_str(), "r" );
723  if( trace.lgTrace && scheme != AS_SILENT_TRY )
724  {
725  fprintf( ioQQQ, " open_data trying to read %s found %c", ptr->c_str(), TorF(handle != NULL) );
726  if( handle != NULL )
727  fprintf( ioQQQ, " used %c", TorF(PathSuccess.size() == 0) );
728  fprintf( ioQQQ, "\n" );
729  }
730  if( handle != NULL )
731  {
732  // don't store path if it is identical to a previous one
733  if( find( PathSuccess.begin(), PathSuccess.end(), *ptr ) == PathSuccess.end() )
734  PathSuccess.push_back( *ptr );
735  fclose( handle );
736  }
737  }
738 
739  if( PathSuccess.size() > 1 && scheme != AS_SILENT_TRY )
740  {
741  fprintf( ioQQQ, "CAUTION: multiple matches for file %s found:\n", fname );
742  for( size_t i=0; i < PathSuccess.size(); ++i )
743  fprintf( ioQQQ, " ==%s==\n", PathSuccess[i].c_str() );
744  fprintf( ioQQQ, "Using the first match.\n" );
745  }
746 
747  // return the first successful match, or an empty string if no match was found
748  return ( PathSuccess.size() > 0 ) ? PathSuccess[0] : "";
749 }
750 
751 FILE* open_data( const char* fname, const char* mode, access_scheme scheme )
752 {
753  DEBUG_ENTRY( "open_data()" );
754 
755  // for mode "r" and "rb" the default is AS_DATA_ONLY, and for all
756  // other modes AS_LOCAL_ONLY since the latter can overwrite the file
757  string m = mode;
758  bool lgRead = ( m == "r" || m == "rb" );
759  if( lgRead && scheme == AS_DEFAULT )
760  scheme = AS_DATA_ONLY;
761 
762  bool lgAbort = ( scheme == AS_DATA_ONLY || scheme == AS_DATA_OPTIONAL ||
763  scheme == AS_LOCAL_DATA || scheme == AS_LOCAL_ONLY );
764 
765  vector<string> PathList;
766  cpu.i().getPathList( fname, PathList, scheme, lgRead );
767 
768  FILE* handle = NULL;
769  string path = check_mult_path( fname, PathList, scheme, lgRead );
770  if( path != "" )
771  handle = sys_fopen( path.c_str(), mode );
772 
773  if( handle == NULL && lgAbort )
774  AbortErrorMessage( fname, PathList, scheme );
775 
776  ++cpu.i().nFileDone;
777 
778  return handle;
779 }
780 
781 void open_data( fstream& stream, const char* fname, ios_base::openmode mode, access_scheme scheme )
782 {
783  DEBUG_ENTRY( "open_data()" );
784 
785  // for mode_r and mode_rb the default is AS_DATA_ONLY, and for all
786  // other modes AS_LOCAL_ONLY since the latter can overwrite the file
787  bool lgRead = ( (mode&ios_base::out) == 0 );
788  if( lgRead && scheme == AS_DEFAULT )
789  scheme = AS_DATA_ONLY;
790 
791  bool lgAbort = ( scheme == AS_DATA_ONLY || scheme == AS_DATA_OPTIONAL ||
792  scheme == AS_LOCAL_DATA || scheme == AS_LOCAL_ONLY );
793 
794  vector<string> PathList;
795  cpu.i().getPathList( fname, PathList, scheme, lgRead );
796 
797  ASSERT( !stream.is_open() );
798  string path = check_mult_path( fname, PathList, scheme, lgRead );
799  if( path != "" )
800  stream.open( path.c_str(), mode );
801 
802  if( !stream.is_open() && lgAbort )
803  AbortErrorMessage( fname, PathList, scheme );
804 
805  ++cpu.i().nFileDone;
806 }
807 
808 MPI_File open_data( const char* fname, int mode, access_scheme scheme )
809 {
810  DEBUG_ENTRY( "open_data()" );
811 
812  // for mpi_mode_r the default is AS_DATA_ONLY, and for all
813  // other modes AS_LOCAL_ONLY since the latter can overwrite the file
814  bool lgRead = ( mode == mpi_mode_r );
815  if( lgRead && scheme == AS_DEFAULT )
816  scheme = AS_DATA_ONLY;
817 
818  bool lgAbort = ( scheme == AS_DATA_ONLY || scheme == AS_DATA_OPTIONAL ||
819  scheme == AS_LOCAL_DATA || scheme == AS_LOCAL_ONLY );
820 
821  vector<string> PathList;
822  cpu.i().getPathList( fname, PathList, scheme, lgRead );
823 
824  int err = MPI_ERR_INTERN;
825  MPI_File fh = MPI_FILE_NULL;
826  string path = check_mult_path( fname, PathList, scheme, lgRead );
827  if( path != "" )
828  err = MPI_File_open( MPI_COMM_WORLD, const_cast<char*>(path.c_str()), mode, MPI_INFO_NULL, &fh );
829 
830  if( err != MPI_SUCCESS && lgAbort )
831  AbortErrorMessage( fname, PathList, scheme );
832 
833  ++cpu.i().nFileDone;
834 
835  return fh;
836 }
837 
838 void check_data( const char* fname, access_scheme scheme )
839 {
840  DEBUG_ENTRY( "check_data()" );
841 
842  if( !( t_version::Inst().lgRelease || t_version::Inst().lgReleaseBranch ) )
843  return;
844 
845  map<string,string>::const_iterator ptr = cpu.i().md5sum_expct.find( fname );
846  if( ptr != cpu.i().md5sum_expct.end() )
847  {
848  string md5sum = MD5datafile( fname, scheme );
849  if( md5sum != ptr->second )
850  {
851  fprintf( ioQQQ, "Warning: using non-standard data in %s: ", fname );
852  fprintf( ioQQQ, "checksum %s != %s (expected)\n", md5sum.c_str(), ptr->second.c_str() );
853  }
854  }
855 }
856 
863 {
864  if( sizeof(sys_float) == 4 )
865  *reinterpret_cast<int32*>(&x) = cpu.i().Float_SNaN_Value;
866  else
867  x = -FLT_MAX;
868 }
869 
870 void set_NaN(sys_float x[], /* x[n] */
871  long n)
872 {
873  long i;
874 
875  if( sizeof(sys_float) == 4 )
876  {
877  int32 *y = reinterpret_cast<int32*>(x);
878  for( i=0; i < n; i++ )
879  *y++ = cpu.i().Float_SNaN_Value;
880  }
881  else
882  {
883  for( i=0; i < n; i++ )
884  x[i] = -FLT_MAX;
885  }
886 }
887 
888 void set_NaN(double &x)
889 {
890  if( sizeof(double) == 8 )
891  {
892 # ifdef HAVE_INT64
893  *reinterpret_cast<int64*>(&x) = cpu.i().Double_SNaN_Value;
894 # else
895  int32 *y = reinterpret_cast<int32*>(&x);
896  *y++ = cpu.i().Double_SNaN_Value[0];
897  *y = cpu.i().Double_SNaN_Value[1];
898 # endif
899  }
900  else
901  x = -DBL_MAX;
902 }
903 
904 /* set_NaN - set NaN */
905 void set_NaN(double x[], /* x[n] */
906  long n)
907 {
908  long i;
909 
910  if( sizeof(double) == 8 )
911  {
912 # ifdef HAVE_INT64
913  int64 *y = reinterpret_cast<int64*>(x);
914  for( i=0; i < n; i++ )
915  *y++ = cpu.i().Double_SNaN_Value;
916 # else
917  int32 *y = reinterpret_cast<int32*>(x);
918  for( i=0; i < n; i++ )
919  {
920  *y++ = cpu.i().Double_SNaN_Value[0];
921  *y++ = cpu.i().Double_SNaN_Value[1];
922  }
923 # endif
924  }
925  else
926  {
927  for( i=0; i < n; i++ )
928  x[i] = -DBL_MAX;
929  }
930 }
931 
933 bool MyIsnan(const sys_float &x)
934 {
935  if( sizeof(sys_float) == 4 && FLT_MAX_EXP-FLT_MIN_EXP+3 == 256 )
936  {
937  const int32 *p = reinterpret_cast<const int32*>(&x);
938  int32 r = *p & 0x7f800000; r ^= 0x7f800000;
939  int32 s = *p & 0x007fffff;
940  return ( r == 0 && s != 0 );
941  }
942  else
943  /* we don't understand this CPU */
944  return false;
945 }
946 
948 bool MyIsnan(const double &x)
949 {
950  if( sizeof(double) == 8 && DBL_MAX_EXP-DBL_MIN_EXP+3 == 2048 )
951  {
952 # ifdef HAVE_INT64
953  const int64 *p = reinterpret_cast<const int64*>(&x);
954  int64 r = *p & INT64_LIT(0x7ff0000000000000); r ^= INT64_LIT(0x7ff0000000000000);
955  int64 s = *p & INT64_LIT(0x000fffffffffffff);
956  return ( r == 0 && s != 0 );
957 # else
958  const int32 *p = reinterpret_cast<const int32*>(&x);
959  if( cpu.i().little_endian() )
960  {
961  int32 r = p[1] & 0x7ff00000; r ^= 0x7ff00000;
962  int32 s = p[1] & 0x000fffff; s |= p[0];
963  return ( r == 0 && s != 0 );
964  }
965  else if( cpu.i().big_endian() )
966  {
967  int32 r = p[0] & 0x7ff00000; r ^= 0x7ff00000;
968  int32 s = p[0] & 0x000fffff; s |= p[1];
969  return ( r == 0 && s != 0 );
970  }
971  else
972  /* we don't understand this CPU */
973  return false;
974 # endif
975  }
976  else
977  /* we don't understand this CPU */
978  return false;
979 }
long n_rank
Definition: cpu.h:330
map< string, string > md5sum_expct
Definition: cpu.h:339
FILE * open_data(const char *fname, const char *mode, access_scheme scheme)
Definition: cpu.cpp:751
static void signal_handler(int sig)
Definition: cpu.cpp:540
#define NORETURN
Definition: cpu.h:451
bool p_lgMPISingleRankMode
Definition: cpu.h:328
NORETURN void TotalInsanity(void)
Definition: service.cpp:1067
double test_double
Definition: cpu.h:301
union t_cpu_i::@4 endian
void set_NaN(sys_float &x)
Definition: cpu.cpp:862
t_cpu_i & i()
Definition: cpu.h:415
FILE * sys_fopen(const char *path, const char *mode)
Definition: cddefines.h:131
int32 Double_SNaN_Value[2]
Definition: cpu.h:307
char TorF(bool l)
Definition: cddefines.h:753
int MPI_ERR_INTERN
void getPathList(const char *fname, vector< string > &PathList, access_scheme scheme, bool lgRead) const
Definition: cpu.cpp:560
access_scheme
Definition: cpu.h:262
Definition: cpu.h:290
const int STDLEN
Definition: cpu.h:252
static t_cpu_i * m_i
Definition: cpu.h:413
void * MPI_File
Definition: mpi_utilities.h:74
bool big_endian() const
Definition: cpu.h:351
bool MyIsnan(const sys_float &x)
Definition: cpu.cpp:933
FILE * ioQQQ
Definition: cddefines.cpp:7
string MD5datafile(const char *fnam, access_scheme scheme)
char HostName[STDLEN]
Definition: cpu.h:332
void getMD5sums(const char *fname)
Definition: cpu.cpp:609
bool little_endian() const
Definition: cpu.h:352
static t_version & Inst()
Definition: cddefines.h:209
#define MPI_File_open(V, W, X, Y, Z)
Definition: mpi_utilities.h:91
~t_cpu()
Definition: cpu.cpp:56
t_trace trace
Definition: trace.cpp:5
long n_avail_CPU
Definition: cpu.h:319
const ios_base::openmode mode_r
Definition: cpu.h:267
#define STATIC
Definition: cddefines.h:118
bool lgTrace
Definition: trace.h:12
int MPI_SUCCESS
int nFileDone
Definition: cpu.h:337
#define EXIT_FAILURE
Definition: cddefines.h:168
float sys_float
Definition: cddefines.h:127
t_cpu_i()
Definition: cpu.cpp:65
void printDataPath() const
Definition: cpu.cpp:552
int32 Float_SNaN_Value
Definition: cpu.h:303
bool firstOpen() const
Definition: cpu.h:398
#define cdEXIT(FAIL)
Definition: cddefines.h:484
char p_chDirSeparator
Definition: cpu.h:336
MPI_File MPI_FILE_NULL
vector< string > p_exit_status
Definition: cpu.h:344
static int cpu_count
Definition: cpu.cpp:48
STATIC string check_mult_path(const char *fname, const vector< string > &PathList, access_scheme scheme, bool lgRead)
Definition: cpu.cpp:706
#define ASSERT(exp)
Definition: cddefines.h:617
FILE * ioPrnErr
Definition: cddefines.cpp:9
#define DEBUG_ENTRY(funcname)
Definition: cddefines.h:729
void set_signal_handlers()
Definition: cpu.cpp:505
bool p_lgAssertAbort
Definition: cpu.h:316
int fprintf(const Output &stream, const char *format,...)
Definition: service.cpp:1217
bool lgPrnErr
Definition: cddefines.cpp:13
void Split(const string &str, const string &sep, vector< string > &lst, split_mode mode)
Definition: service.cpp:108
int32 i
Definition: cpu.h:297
void enable_traps() const
Definition: cpu.cpp:330
sys_float test_float
Definition: cpu.h:300
static t_cpu cpu
Definition: cpu.h:423
bool p_lgMPI
Definition: cpu.h:323
bool FindAndReplace(string &str, const string &substr, const string &newstr)
Definition: service.h:29
vector< string > chSearchPath
Definition: cpu.h:334
t_cpu()
Definition: cpu.cpp:49
STATIC NORETURN void AbortErrorMessage(const char *fname, const vector< string > &PathList, access_scheme scheme)
Definition: cpu.cpp:654
bool lgAbort
Definition: cddefines.cpp:10
void check_data(const char *fname, access_scheme scheme)
Definition: cpu.cpp:838
int mpi_mode_r
FILE * ioStdin
Definition: cddefines.cpp:8