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