1 /*!
2  *
3  * \file utils.cpp
4  * \brief Utility routines
5  * \details TODO A more detailed description of this routine.
6  * \author David Favis-Mortlock
7  * \author Andres Payo
8  * \author Jim Hall
9  * \date 2017
10  * \copyright GNU General Public License
11  *
12  */
13 
14 /*==============================================================================================================================
15 
16  This file is part of CliffMetrics, the Coastal Modelling Environment.
17 
18  CliffMetrics is free software; you can redistribute it and/or modify it under the terms of the GNU General Public  License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
19 
20  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 
24 ==============================================================================================================================*/
25 //#include <assert.h>
26 
27 #ifdef _WIN32
28    #include <windows.h>             // needed for CalcProcessStats()
29    #include <psapi.h>               // not available if compiling under Win?
30    #include <io.h>                  // for isatty()
31 #else
32    #include <sys/resource.h>        // needed for CalcProcessStats()
33    #include <unistd.h>
34 #endif
35 
36 #include <iostream>
37 using std::cout;
38 using std::cerr;
39 using std::endl;
40 using std::ios;
41 
42 #include <iomanip>
43 using std::setiosflags;
44 using std::resetiosflags;
45 using std::setprecision;
46 using std::setw;
47 
48 #if !defined(_SAGA_MSW) && !defined(_SAGA_LINUX)
49 #include <gdal_priv.h>
50 #elif defined(_OPENMP)
51 #include <omp.h>
52 #endif
53 
54 #include "cliffmetrics.h"
55 #include "delineation.h"
56 
57 
58 /*==============================================================================================================================
59 
60  Handles command-line parameters
61 
62 ==============================================================================================================================*/
nHandleCommandLineParams(int nArg,char * pcArgv[])63 int CDelineation::nHandleCommandLineParams(int nArg, char* pcArgv[])
64 {
65 #if !defined(_SAGA_MSW) && !defined(_SAGA_LINUX)
66    for (int i = 1; i < nArg; i++)
67    {
68       string strArg = pcArgv[i];
69 #ifdef _WIN32
70       // Swap any forward slashes to backslashes
71       strArg = pstrChangeToBackslash(&strArg);
72 #endif
73 
74       // change to lower case
75       strArg = strToLower(&strArg);
76 
77       if (strArg.find("--gdal") != string::npos)
78       {
79          // User wants to know what GDAL raster drivers are available
80          cout << GDALDRIVERS << endl << endl;
81 
82          for (int i = 0; i < GDALGetDriverCount(); i++ )
83          {
84             GDALDriverH hDriver = GDALGetDriver(i);
85 
86             string strTmp(GDALGetDriverShortName(hDriver));
87             strTmp.append("          ");
88             strTmp.append(GDALGetDriverLongName(hDriver));
89 
90             cout << strTmp << endl;
91          }
92          return (RTN_HELPONLY);
93       }
94 
95       else if (strArg.find("--about") != string::npos)
96       {
97          // User wants information about CliffMetrics
98          cout << ABOUT << endl;
99          cout << THANKS << endl;
100 
101          return (RTN_HELPONLY);
102       }
103 
104       // TODO handle other command line parameters e.g. path to .ini file, path to datafile
105 
106       else
107       {
108          // Display usage information
109          cout << USAGE << endl;
110          cout << USAGE1 << endl;
111          cout << USAGE2 << endl;
112          cout << USAGE3 << endl;
113          cout << USAGE4 << endl;
114          cout << USAGE5 << endl;
115 
116          return (RTN_HELPONLY);
117       }
118    }
119 #endif // #if !defined(_SAGA_MSW) && !defined(_SAGA_LINUX)
120 
121    return RTN_OK;
122 }
123 
124 
125 /*==============================================================================================================================
126 
127  Tells the user that we have started the simulation
128 
129 ==============================================================================================================================*/
AnnounceStart(void)130 void CDelineation::AnnounceStart(void)
131 {
132    cout << endl << PROGNAME << " for " << PLATFORM << " " << strGetBuild() << endl;
133 }
134 
135 
136 /*==============================================================================================================================
137 
138  Starts the clock ticking
139 
140 ==============================================================================================================================*/
StartClock(void)141 void CDelineation::StartClock(void)
142 {
143    // First start the 'CPU time' clock ticking
144    if (static_cast<clock_t>(-1) == clock())
145    {
146       // There's a problem with the clock, but continue anyway
147       LogStream << WARN << "CPU time not available" << endl;
148       m_dCPUClock = -1;
149    }
150    else
151    {
152       // All OK, so get the time in m_dClkLast (this is needed to check for clock rollover on long runs)
153       m_dClkLast = static_cast<double>(clock());
154       m_dClkLast -= CLOCK_T_MIN;       // necessary if clock_t is signed to make m_dClkLast unsigned
155    }
156 
157    // And now get the actual time we started
158    time(&m_tSysStartTime);
159 }
160 
161 
162 /*==============================================================================================================================
163 
164  Finds the folder (directory) in which the CliffMetrics executable is located
165 
166 ==============================================================================================================================*/
bFindExeDir(char * pcArg)167 bool CDelineation::bFindExeDir(char* pcArg)
168 {
169 #if !defined(_SAGA_MSW) && !defined(_SAGA_LINUX)
170    string strTmp;
171    char szBuf[BUFSIZE];
172 
173 #ifdef _WIN32
174    if (0 != GetModuleFileName(NULL, szBuf, BUFSIZE))
175       strTmp = szBuf;
176    else
177       // It failed, so try another approach
178       strTmp = pcArg;
179 #else
180 //   char* pResult = getcwd(szBuf, BUFSIZE);          // Used to use this, but what if cwd is not the same as the CliffMetrics dir?
181 
182    if (-1 != readlink("/proc/self/exe", szBuf, BUFSIZE))
183       strTmp = szBuf;
184    else
185       // It failed, so try another approach
186       strTmp = pcArg;
187 #endif
188 
189    // Neither approach has worked, so give up
190    if (strTmp.empty())
191       return false;
192 
193    // It's OK, so trim off the executable's name
194    int nPos = strTmp.find_last_of(PATH_SEPARATOR);
195    m_strCLIFFDir = strTmp.substr(0, nPos+1);            // Note that this must be terminated with a backslash
196 #endif // #if !defined(_SAGA_MSW) && !defined(_SAGA_LINUX)
197 
198    return true;
199 }
200 
201 
202 /*==============================================================================================================================
203 
204  Tells the user about the licence
205 
206 ==============================================================================================================================*/
AnnounceLicence(void)207 void CDelineation::AnnounceLicence(void)
208 {
209    cout << COPYRIGHT << endl << endl;
210    cout << LINE << endl;
211    cout << DISCLAIMER1 << endl;
212    cout << DISCLAIMER2 << endl;
213    cout << DISCLAIMER3 << endl;
214    cout << DISCLAIMER4 << endl;
215    cout << DISCLAIMER5 << endl;
216    cout << DISCLAIMER6 << endl;
217    cout << LINE << endl << endl;
218 
219    // Note endl not needed, ctime() seems to always output a trailing <cr>
220    cout << STARTNOTICE << strGetComputerName() << " on " << ctime(&m_tSysStartTime);
221    cout << INITNOTICE << endl;
222 }
223 
224 /*==============================================================================================================================
225 
226  Given a string containing time units, this returns the appropriate multiplier
227 
228 ==============================================================================================================================*/
dGetTimeMultiplier(string const * strIn)229 double CDelineation::dGetTimeMultiplier(string const* strIn)
230 {
231    // First decide what the time units are
232    int nTimeUnits = nDoTimeUnits(strIn);
233 
234    // Then return the correct multiplier, since m_dTimeStep is in hours
235    switch (nTimeUnits)
236    {
237       case TIME_UNKNOWN:
238          return TIME_UNKNOWN;
239          break;
240 
241       case TIME_HOURS:
242          return 1;                     // Multiplier for hours
243          break;
244 
245       case TIME_DAYS:
246          return 24;                    // Multiplier for days -> hours
247          break;
248 
249       case TIME_MONTHS:
250          return 24 * 30.416667;        // Multiplier for months -> hours (assume 30 + 5/12 day months, no leap years)
251          break;
252 
253       case TIME_YEARS:
254          return 24 * 365.25;           // Multiplier for years -> hours
255          break;
256    }
257 
258    return 0;
259 }
260 
261 /*==============================================================================================================================
262 
263  Given a string containing time units, this sets up the appropriate multiplier and display units for the simulation
264 
265 ==============================================================================================================================*/
nDoSimulationTimeMultiplier(string const * strIn)266 int CDelineation::nDoSimulationTimeMultiplier(string const* strIn)
267 {
268    // First decide what the time units are
269    int nTimeUnits = nDoTimeUnits(strIn);
270 
271    // Next set up the correct multiplier, since m_dTimeStep is in hours
272    switch (nTimeUnits)
273    {
274       case TIME_UNKNOWN:
275          return RTN_ERR_TIMEUNITS;
276          break;
277 
278       case TIME_HOURS:
279          m_dDurationUnitsMult = 1;                     // Multiplier for hours
280          m_strDurationUnits = "hours";
281          break;
282 
283       case TIME_DAYS:
284          m_dDurationUnitsMult = 24;                    // Multiplier for days -> hours
285          m_strDurationUnits = "days";
286          break;
287 
288       case TIME_MONTHS:
289          m_dDurationUnitsMult = 24 * 30.416667;        // Multiplier for months -> hours (assume 30 + 5/12 day months, no leap years)
290          m_strDurationUnits = "months";
291          break;
292 
293       case TIME_YEARS:
294          m_dDurationUnitsMult = 24 * 365.25;           // Multiplier for years -> hours
295          m_strDurationUnits = "years";
296          break;
297    }
298 
299    return RTN_OK;
300 }
301 
302 /*==============================================================================================================================
303 
304  This finds time units in a string
305 
306 ==============================================================================================================================*/
nDoTimeUnits(string const * strIn)307 int CDelineation::nDoTimeUnits(string const* strIn)
308 {
309    if (strIn->find('h') != string::npos)            // Search for 'hours'
310       return TIME_HOURS;
311    else if (strIn->find('d') != string::npos)       // Search for 'days'
312       return TIME_DAYS;
313    else if (strIn->find('m') != string::npos)       // Search for 'months'
314       return TIME_MONTHS;
315    else if (strIn->find('y') != string::npos)       // Search for 'years'
316       return TIME_YEARS;
317    else
318       return TIME_UNKNOWN;
319 }
320 
321 /*==============================================================================================================================
322 
323  Opens the log file
324 
325 ==============================================================================================================================*/
bOpenLogFile(void)326 bool CDelineation::bOpenLogFile(void)
327 {
328    // Open in binary mode if just checking random numbers
329 #ifdef RANDCHECK
330    LogStream.open(m_strLogFile.c_str(), ios::out | ios::binary | ios::trunc);
331 #else
332    LogStream.open(m_strLogFile.c_str(), ios::out | ios::trunc);
333 #endif
334 
335    if (! LogStream)
336    {
337       // Error, cannot open log file
338       cerr << ERR << "cannot open " << m_strLogFile << " for output" << endl;
339       return false;
340    }
341 
342    return true;
343 }
344 /*==============================================================================================================================
345 
346  Tells the user that we are now reading the DTM file
347 
348 ==============================================================================================================================*/
AnnounceReadDTM(void) const349 void CDelineation::AnnounceReadDTM(void) const
350 {
351    // Tell the user what is happening
352 #ifdef _WIN32
353    cout << READDTM << pstrChangeToForwardSlash(&m_strDTMFile) << endl;
354 #else
355    cout << READDTM << m_strDTMFile << endl;
356 #endif
357 }
358 
359 
360 /*==============================================================================================================================
361 
362  Tells the user that we are now allocating memory
363 
364 ==============================================================================================================================*/
AnnounceAllocateMemory(void)365 void CDelineation::AnnounceAllocateMemory(void)
366 {
367    cout << ALLOCATEMEMORY << endl;
368 }
369 
370 /*==============================================================================================================================
371 
372  Tell the user that we are now reading the user defined Coastline
373 
374 ==============================================================================================================================*/
AnnounceReadUserCoastLine(void)375  void CDelineation::AnnounceReadUserCoastLine(void)
376  {
377 #ifdef _WIN32
378    cout << READVECTORFILES << pstrChangeToForwardSlash(&m_strDTMFile) << endl;
379 #else
380    cout << READVECTORFILES << m_strInitialCoastlineFile << endl;
381 #endif
382  }
383 
384 
385 
386 /*==============================================================================================================================
387 
388  Tells the user that we are now initializing
389 
390 ==============================================================================================================================*/
AnnounceInitializing(void)391 void CDelineation::AnnounceInitializing(void)
392 {
393    // Tell the user what is happening
394    cout << INITIALIZING << endl;
395 }
396 
397 
398 /*==============================================================================================================================
399 
400  Tell the user that the simulation is now running
401 
402 ==============================================================================================================================*/
AnnounceIsRunning(void)403 void CDelineation::AnnounceIsRunning(void)
404 {
405    cout << RUNNOTICE << endl;
406 }
407 
408 /*==============================================================================================================================
409 
410  Return a space-separated string containing the names of the raster GIS output files
411 
412 ==============================================================================================================================*/
strListRasterFiles(void) const413 string CDelineation::strListRasterFiles(void) const
414 {
415    string strTmp;
416 
417    if (m_bRasterCoastlineSave)
418    {
419       strTmp.append(RASTER_COAST_NAME);
420       strTmp.append(", ");
421    }
422 
423    if (m_bRasterNormalSave)
424    {
425       strTmp.append(RASTER_COAST_NORMAL_NAME);
426       strTmp.append(", ");
427    }
428 
429 
430    // remove the trailing comma and space
431    strTmp.resize(strTmp.size()-2);
432 
433    return strTmp;
434 }
435 
436 /*==============================================================================================================================
437 
438  Return a space-separated string containing the names of the vector GIS output files
439 
440 ==============================================================================================================================*/
strListVectorFiles(void) const441 string CDelineation::strListVectorFiles(void) const
442 {
443    string strTmp;
444 
445    if (m_bCoastSave)
446    {
447       strTmp.append(VECTOR_COAST_CODE);
448       strTmp.append(", ");
449    }
450 
451    if (m_bNormalsSave)
452    {
453       strTmp.append(VECTOR_NORMALS_CODE);
454       strTmp.append(", ");
455    }
456 
457    if (m_bInvalidNormalsSave)
458    {
459       strTmp.append(VECTOR_INVALID_NORMALS_CODE);
460       strTmp.append(", ");
461    }
462 
463    if (m_bCoastCurvatureSave)
464    {
465       strTmp.append(VECTOR_COAST_CURVATURE_CODE);
466       strTmp.append(", ");
467    }
468 
469    if (m_bCliffTopSave)
470    {
471       strTmp.append(VECTOR_CLIFF_TOP_CODE);
472       strTmp.append(", ");
473    }
474 
475    if (m_bCliffToeSave)
476    {
477       strTmp.append(VECTOR_CLIFF_TOE_CODE);
478       strTmp.append(", ");
479    }
480 
481    // remove the trailing comma and space
482    strTmp.resize(strTmp.size()-2);
483 
484    return strTmp;
485 }
486 
487 
488 
489 /*==============================================================================================================================
490 
491  Returns a string, hopefully giving the name of the computer on which the simulation is running
492 
493 ==============================================================================================================================*/
strGetComputerName(void)494 string CDelineation::strGetComputerName(void)
495 {
496    string strComputerName;
497 
498 #ifdef _WIN32
499    // Being compiled to run under Windows, either by MS VC++, Borland C++, or Cygwin
500    strComputerName = getenv("COMPUTERNAME");
501 #else
502    // Being compiled for another platform; assume for Linux-Unix
503    char szHostName[BUFSIZE] = "";
504    gethostname(szHostName, BUFSIZE);
505 
506    strComputerName = szHostName;
507    if (strComputerName.empty())
508       strComputerName = "Unknown Computer";
509 #endif
510 
511    return strComputerName;
512 }
513 
514 /*==============================================================================================================================
515 
516  Resets the CPU clock timer to prevent it 'rolling over', as can happen during long runs. This is a particularly problem under Unix systems where the value returned by clock() is defined in microseconds (for compatibility with systems that have CPU clocks with much higher resolution) i.e. CLOCKS_PER_SEC is 1000000 rather than the more usual 1000. In this case, the value returned from clock() will wrap around after accumulating only 2147 seconds of CPU time (about 36 minutes).
517 
518 ==============================================================================================================================*/
DoCPUClockReset(void)519 void CDelineation::DoCPUClockReset(void)
520 {
521    if (static_cast<clock_t>(-1) == clock())
522    {
523       // Error
524       LogStream << "CPU time not available" << endl;
525       m_dCPUClock = -1;
526       return;
527    }
528 
529    // OK, so carry on
530    double dClkThis = static_cast<double>(clock());
531    dClkThis -= CLOCK_T_MIN;   // necessary when clock_t is signed, to make dClkThis unsigned
532 
533    if (dClkThis < m_dClkLast)
534    {
535       // Clock has 'rolled over'
536       m_dCPUClock += (CLOCK_T_RANGE + 1 - m_dClkLast);   // this elapsed before rollover
537       m_dCPUClock += dClkThis;                           // this elapsed after rollover
538 
539 #ifdef CLOCKCHECK
540       // For debug purposes
541       LogStream << "Rolled over: dClkThis=" << dClkThis << " m_dClkLast=" << m_dClkLast << endl << "\t" << " before rollover=" << (CLOCK_T_RANGE + 1 - m_dClkLast) << endl << "\t" << " after rollover=" << dClkThis << endl << "\t" << " ADDED=" << (CLOCK_T_RANGE + 1 - m_dClkLast + dClkThis) << endl;
542 #endif
543    }
544    else
545    {
546       // No rollover
547       m_dCPUClock += (dClkThis - m_dClkLast);
548 
549 #ifdef CLOCKCHECK
550       // For debug purposes
551       LogStream << "No rollover: dClkThis=" << dClkThis << " m_dClkLast=" << m_dClkLast << " ADDED=" << dClkThis - m_dClkLast << endl;
552 #endif
553    }
554 
555    // Reset for next time
556    m_dClkLast = dClkThis;
557 }
558 
559 
560 /*==============================================================================================================================
561 
562  Announce the end of the simulation
563 
564 ==============================================================================================================================*/
AnnounceSimEnd(void)565 void CDelineation::AnnounceSimEnd(void)
566 {
567    cout << endl << FINALOUTPUT << endl;
568 }
569 
570 
571 /*==============================================================================================================================
572 
573  Calculates and displays time elapsed in terms of CPU time and real time, also calculates time per timestep in terms of both CPU time
574  and real time
575 
576 ==============================================================================================================================*/
CalcTime(double const dRunLength)577 void CDelineation::CalcTime(double const dRunLength)
578 {
579    // Reset CPU count for last time
580    DoCPUClockReset();
581 
582    if (m_dCPUClock != -1)
583    {
584       // Calculate CPU time in secs
585       double dDuration = m_dCPUClock/CLOCKS_PER_SEC;
586 
587       // And write CPU time out to OutStream and LogStream
588       OutStream << "CPU time elapsed: " << strDispTime(dDuration, false, true);
589       LogStream << "CPU time elapsed: " << strDispTime(dDuration, false, true);
590 
591       // Calculate CPU time per timestep
592       double fPerTimestep = dDuration / m_ulTotTimestep;
593 
594       // And write CPU time per timestep to OutStream and LogStream
595       OutStream << setiosflags(ios::fixed) << setprecision(4) << " (" << fPerTimestep << " per timestep)" << endl;
596       LogStream << setiosflags(ios::fixed) << setprecision(4) << " (" << fPerTimestep << " per timestep)" << endl;
597 
598       // Calculate ratio of CPU time to time simulated
599       OutStream << resetiosflags(ios::floatfield);
600       OutStream << setiosflags(ios::fixed) << setprecision(0) << "In terms of CPU time, this is ";
601       LogStream << resetiosflags(ios::floatfield);
602       LogStream << setiosflags(ios::fixed) << setprecision(0) << "In terms of CPU time, this is ";
603       if (dDuration > dRunLength)
604       {
605          OutStream << dDuration / dRunLength << " x slower than reality" << endl;
606          LogStream << dDuration / dRunLength << " x slower than reality" << endl;
607       }
608       else
609       {
610          OutStream << dRunLength / dDuration << " x faster than reality" << endl;
611          LogStream << dRunLength / dDuration << " x faster than reality" << endl;
612       }
613    }
614 
615    // Now calculate actual time of run (only really useful if run is a background batch job e.g. under Unix)
616    time(&m_tSysEndTime);
617 
618    // Calculate run time
619    double dDuration = difftime(m_tSysEndTime, m_tSysStartTime);
620 
621    // And write run time out to OutStream and LogStream
622    OutStream << "Run time elapsed: " << strDispTime(dDuration, false, false);
623    LogStream << "Run time elapsed: " << strDispTime(dDuration, false, false);
624 
625 }
626 
627 /*==============================================================================================================================
628 
629  strDispSimTime returns a string formatted as year Julian_day hour, given a parameter in hours
630 
631 ==============================================================================================================================*/
strDispSimTime(const double dTimeIn)632 string CDelineation::strDispSimTime(const double dTimeIn)
633 {
634    // Make sure no negative times
635    double dTime = tMax(dTimeIn, 0.0);
636 
637    string strTime;
638 
639    unsigned long ulTimeIn = static_cast<unsigned long>(floor(dTime));
640 
641    // Constants
642    double const dHoursInYear = 24 * 365.25;
643    unsigned long const ulHoursInDay = 24;
644 
645    // Display years
646    if (ulTimeIn >= dHoursInYear)
647    {
648       unsigned long ulYears = static_cast<unsigned long>(dRound(ulTimeIn / dHoursInYear));
649       ulTimeIn -= static_cast<unsigned long>(dRound(ulYears * dHoursInYear));
650 
651       char szTmp[6] = "";
652       strTime = pszTrimLeft(pszLongToSz(ulYears, szTmp, 6));
653       strTime.append("y ");
654    }
655    else
656       strTime = "0y ";
657 
658    // Display Julian days
659    if (ulTimeIn >= ulHoursInDay)
660    {
661       unsigned long ulJDays = ulTimeIn / ulHoursInDay;
662       ulTimeIn -= (ulJDays * ulHoursInDay);
663 
664       char szTmp[4] = "";
665       pszLongToSz(ulJDays, szTmp, 4);              // Pad with zeros
666       strTime.append(szTmp);
667       strTime.append("d ");
668    }
669    else
670       strTime.append("000 d ");
671 
672    // Display hours
673    char szTmp[3] = "";
674    pszLongToSz(ulTimeIn, szTmp, 3);                // Pad with zeros
675    strTime.append(szTmp);
676    strTime.append("h");
677 
678    return strTime;
679 }
680 
681 
682 /*==============================================================================================================================
683 
684  strDispTime returns a string formatted as h:mm:ss, given a parameter in seconds, with rounding and fractions of a second if desired
685 
686 ==============================================================================================================================*/
strDispTime(const double dTimeIn,const bool bRound,const bool bFrac)687 string CDelineation::strDispTime(const double dTimeIn, const bool bRound, const bool bFrac)
688 {
689    // Make sure no negative times
690    double dTime = tMax(dTimeIn, 0.0);
691 
692    string strTime;
693 
694    if (bRound)
695       dTime = dRound(dTime);
696 
697    unsigned long ulTimeIn = static_cast<unsigned long>(floor(dTime));
698    dTime -= ulTimeIn;
699 
700    // Hours
701    if (ulTimeIn >= 3600)
702    {
703       // Display some hours
704       unsigned long ulHours = ulTimeIn / 3600ul;
705       ulTimeIn -= (ulHours * 3600ul);
706 
707       char szTmp[6] = "";
708       strTime = pszTrimLeft(pszLongToSz(ulHours, szTmp, 6, 10));
709       strTime.append(":");
710    }
711    else
712       strTime = "0:";
713 
714    // Minutes
715    if (ulTimeIn >= 60)
716    {
717       // display some minutes
718       unsigned long ulMins = ulTimeIn / 60ul;
719       ulTimeIn -= (ulMins * 60ul);
720 
721       char szTmp[3] = "";
722       pszLongToSz(ulMins, szTmp, 3);            // Pad with zeros
723       strTime.append(szTmp);
724       strTime.append(":");
725    }
726    else
727       strTime.append("00:");
728 
729    // Seconds
730    char szTmp[3] = "";
731    pszLongToSz(ulTimeIn, szTmp, 3);             // Pad with zeros
732    strTime.append(szTmp);
733 
734    if (bFrac)
735    {
736       // Fractions of a second
737       pszLongToSz(static_cast<unsigned long>(dTime * 100), szTmp, 3);
738       strTime.append(".");
739       strTime.append(szTmp);
740    }
741 
742    return strTime;
743 }
744 
745 
746 /*==============================================================================================================================
747 
748  Returns the date and time on which the program was compiled
749 
750 ==============================================================================================================================*/
strGetBuild(void)751 string CDelineation::strGetBuild(void)
752 {
753    string strBuild("(");
754    strBuild.append(__TIME__);
755    strBuild.append(" ");
756    strBuild.append(__DATE__);
757 #ifdef _DEBUG
758    strBuild.append(" DEBUG");
759 #endif
760    strBuild.append(" build)");
761 
762    return strBuild;
763 }
764 
765 
766 /*==============================================================================================================================
767 
768  Displays information regarding the progress of the simulation
769 
770 ==============================================================================================================================*/
AnnounceProgress(void)771 void CDelineation::AnnounceProgress(void)
772 {
773    if (isatty(1))
774    {
775       // Stdout is connected to a tty, so not running as a background job
776       static double sdElapsed = 0;
777 
778       // Update time elapsed and time remaining every nInterval timesteps
779       time_t tNow;
780       time(&tNow);
781 
782       // Calculate time elapsed and remaining
783       sdElapsed = difftime(tNow, m_tSysStartTime);
784 
785       // Tell the user about progress (note need to make several separate calls to cout here, or MS VC++ compiler appears to get confused)
786       cout << strDispTime(sdElapsed, false, false) << ")  ";
787       cout.flush();
788    }
789 }
790 
791 
ulGetTausworthe(unsigned long const ulS,unsigned long const ulA,unsigned long const ulB,unsigned long const ulC,unsigned long const ulD)792 unsigned long CDelineation::ulGetTausworthe(unsigned long const ulS, unsigned long const ulA, unsigned long const ulB, unsigned long const ulC, unsigned long const ulD)
793 {
794    return (((ulS & ulC) << ulD) & MASK) ^ ((((ulS << ulA) & MASK) ^ ulS) >> ulB);
795 }
796 
797 
dGetRand0d1(void)798 double CDelineation::dGetRand0d1(void)
799 {
800    // Uses ulGetRand0() to return a double precision floating point number uniformly distributed in the range [0, 1) i.e. includes 0.0 but excludes 1.0. Based on a routine in taus.c from gsl-1.2
801    return (ulGetRand0() / 4294967296.0);
802 }
803 
804 // int CDelineation::nGetRand0To(int const nBound)
805 // {
806 //    // Uses ulGetRand0() to return a random integer uniformly distributed in the range [0, nBound) i.e. includes 0 but excludes nBound
807 //    int nRtn;
808 //    unsigned long ulScale = 4294967295ul / nBound;                 // nBound must be > 1
809 //    do
810 //    {
811 //       nRtn = ulGetRand0() / ulScale;
812 //    }
813 //    while (nRtn >= nBound);
814 //    return (nRtn);
815 // }
816 
817 
nGetRand1To(int const nBound)818 int CDelineation::nGetRand1To(int const nBound)
819 {
820    // As above, but uses ulGetRand1()
821    int nRtn;
822    unsigned long ulScale = 4294967295ul / nBound;                 // nBound must be > 1
823    do
824    {
825       nRtn = ulGetRand1() / ulScale;
826    }
827    while (nRtn >= nBound);
828    return (nRtn);
829 }
830 
831 
832 // double CDelineation::dGetRand0GaussPos(double const dMean, double const dStd)
833 // {
834 //    // Uses ulGetRand0()
835 //    return (tMax((dGetRand0Gaussian() * dStd) + dMean, 0.0));
836 // }
837 
838 
839 /*==============================================================================================================================
840 
841  This calculates and displays process statistics
842 
843 ==============================================================================================================================*/
CalcProcessStats(void)844 void CDelineation::CalcProcessStats(void)
845 {
846    string const NA = "Not available";
847 
848    OutStream << endl;
849    OutStream << "Process statistics" << endl;
850    OutStream << "------------------" << endl;
851 
852 #ifdef _WIN32
853    // First, find out which version of Windows we are running under
854    OSVERSIONINFOEX osvi;
855    BOOL bOsVersionInfoEx;
856 
857    ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));              // fill this much memory with zeros
858    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
859 
860    if (! (bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) &osvi)))
861    {
862       // OSVERSIONINFOEX didn't work so try OSVERSIONINFO instead
863       osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
864 
865       if (! GetVersionEx((OSVERSIONINFO *) &osvi))
866       {
867          // That didn't work either, too risky to proceed so give up
868          OutStream << NA << endl;
869          return;
870       }
871    }
872 
873    // OK, we have Windows version so display it
874    OutStream << "Running under                                \t: ";
875    switch (osvi.dwPlatformId)
876    {
877       case VER_PLATFORM_WIN32_NT:
878          if (osvi.dwMajorVersion <= 4)
879             OutStream << "Windows NT ";
880          else if (5 == osvi.dwMajorVersion && 0 == osvi.dwMinorVersion)
881             OutStream << "Windows 2000 ";
882          else if (5 == osvi.dwMajorVersion && 1 == osvi.dwMinorVersion)
883             OutStream << "Windows XP ";
884          else if (6 == osvi.dwMajorVersion && 0 == osvi.dwMinorVersion)
885             OutStream << "Windows Vista ";
886          else if (6 == osvi.dwMajorVersion && 1 == osvi.dwMinorVersion)
887             OutStream << "Windows 7 ";
888          else if (6 == osvi.dwMajorVersion && 2 == osvi.dwMinorVersion)
889             OutStream << "Windows 8 ";
890          // TODO add info for other Windows version
891          else
892             OutStream << "unknown Windows version ";
893 
894          // Display version, service pack (if any), and build number
895          if (osvi.dwMajorVersion <= 4)
896             // TODO does this still work on 64-bit platforms?
897             OutStream << "version " << osvi.dwMajorVersion << "." << osvi.dwMinorVersion << " " << osvi.szCSDVersion << " (Build " << (osvi.dwBuildNumber & 0xFFFF) << ")" << endl;
898          else
899             // TODO does this still work on 64-bit platforms?
900             OutStream << osvi.szCSDVersion << " (Build " << (osvi.dwBuildNumber & 0xFFFF) << ")" << endl;
901          break;
902 
903       case VER_PLATFORM_WIN32_WINDOWS:
904          if (4 == osvi.dwMajorVersion && 0 == osvi.dwMinorVersion)
905          {
906              OutStream << "Windows 95";
907              if ('C' == osvi.szCSDVersion[1] || 'B' == osvi.szCSDVersion[1])
908                 OutStream << " OSR2";
909              OutStream << endl;
910          }
911          else if (4 == osvi.dwMajorVersion && 10 == osvi.dwMinorVersion)
912          {
913              OutStream << "Windows 98";
914              if ('A' == osvi.szCSDVersion[1])
915                 OutStream << "SE";
916              OutStream << endl;
917          }
918          else if (4 == osvi.dwMajorVersion && 90 == osvi.dwMinorVersion)
919              OutStream << "Windows Me" << endl;
920          else
921              OutStream << "unknown 16-bit Windows version " << endl;
922          break;
923 
924       case VER_PLATFORM_WIN32s:
925          OutStream << "Win32s" << endl;
926          break;
927    }
928 
929    // Now get process timimgs: this only works under 32-bit windows
930    if (VER_PLATFORM_WIN32_NT == osvi.dwPlatformId )
931    {
932       FILETIME ftCreate, ftExit, ftKernel, ftUser;
933       if (GetProcessTimes(GetCurrentProcess(), &ftCreate, &ftExit, &ftKernel, &ftUser))
934       {
935          ULARGE_INTEGER ul;
936          ul.LowPart = ftUser.dwLowDateTime;
937          ul.HighPart = ftUser.dwHighDateTime;
938          OutStream << "Time spent executing user code               \t: " << strDispTime(static_cast<double>(ul.QuadPart) * 1e-7, false, true) << endl;
939          ul.LowPart = ftKernel.dwLowDateTime;
940          ul.HighPart = ftKernel.dwHighDateTime;
941          OutStream << "Time spent executing kernel code             \t: " << strDispTime(static_cast<double>(ul.QuadPart) * 1e-7, false, true) << endl;
942       }
943    }
944    else
945       OutStream << "Process timings                              \t: " << NA << endl;
946 
947    // Finally get more process statistics: this needs psapi.dll, so only proceed if it is present on this system
948 #if !defined(_SAGA_MSW) && !defined(_SAGA_LINUX)
949    HINSTANCE hDLL = LoadLibrary("psapi.dll");
950 #else // #if defined(_SAGA_MSW) || defined(_SAGA_LINUX)
951    HINSTANCE hDLL = LoadLibrary(SG_T("psapi.dll"));
952 #endif // #if defined(_SAGA_MSW) || defined(_SAGA_LINUX)
953    if (hDLL != NULL)
954    {
955       // The dll has been found
956       typedef BOOL (__stdcall* DLLPROC) (HANDLE, PPROCESS_MEMORY_COUNTERS, DWORD);
957       DLLPROC ProcAdd;
958 
959       // Try to get the address of the function we will call
960       ProcAdd = (DLLPROC) GetProcAddress(hDLL, "GetProcessMemoryInfo");
961       if (ProcAdd)
962       {
963          // Address was found
964          PROCESS_MEMORY_COUNTERS pmc;
965 
966          // Now call the function
967          if ((ProcAdd) (GetCurrentProcess(), &pmc, sizeof(pmc)))
968          {
969             OutStream << "Peak working set size                        \t: " << pmc.PeakWorkingSetSize / (1024.0 * 1024.0) << " Mb" << endl;
970             OutStream << "Current working set size                     \t: " << pmc.WorkingSetSize / (1024.0 * 1024.0) << " Mb" << endl;
971             OutStream << "Peak paged pool usage                        \t: " << pmc.QuotaPeakPagedPoolUsage / (1024.0 * 1024.0) << " Mb" << endl;
972             OutStream << "Current paged pool usage                     \t: " << pmc.QuotaPagedPoolUsage / (1024.0 * 1024.0) << " Mb" << endl;
973             OutStream << "Peak non-paged pool usage                    \t: " << pmc.QuotaPeakNonPagedPoolUsage / (1024.0 * 1024.0) << " Mb" << endl;
974             OutStream << "Current non-paged pool usage                 \t: " << pmc.QuotaNonPagedPoolUsage / (1024.0 * 1024.0) << " Mb" << endl;
975             OutStream << "Peak pagefile usage                          \t: " << pmc.PeakPagefileUsage / (1024.0 * 1024.0) << " Mb" << endl;
976             OutStream << "Current pagefile usage                       \t: " << pmc.PagefileUsage / (1024.0 * 1024.0) << " Mb" << endl;
977             OutStream << "No. of page faults                           \t: " << pmc.PageFaultCount << endl;
978          }
979       }
980 
981       // Free the memory used by the dll
982       FreeLibrary(hDLL);
983    }
984 
985 #elif defined __GNUG__
986    rusage ru;
987    if (getrusage(RUSAGE_SELF, &ru) >= 0)
988    {
989       OutStream << "Time spent executing user code               \t: "  << strDispTime(ru.ru_utime.tv_sec, false, true) << endl;
990 //      OutStream << "ru_utime.tv_usec                             \t: " << ru.ru_utime.tv_usec << endl;
991       OutStream << "Time spent executing kernel code             \t: " << strDispTime(ru.ru_stime.tv_sec, false, true) << endl;
992 //      OutStream << "ru_stime.tv_usec                             \t: " << ru.ru_stime.tv_usec << endl;
993 //      OutStream << "Maximum resident set size                    \t: " << ru.ru_maxrss/1024.0 << " Mb" << endl;
994 //      OutStream << "ixrss (???)                                  \t: " << ru.ru_ixrss << endl;
995 //      OutStream << "Sum of rm_asrss (???)                        \t: " << ru.ru_idrss << endl;
996 //      OutStream << "isrss (???)                                  \t: " << ru.ru_isrss << endl;
997       OutStream << "No. of page faults not requiring physical I/O\t: " << ru.ru_minflt << endl;
998       OutStream << "No. of page faults requiring physical I/O    \t: " << ru.ru_majflt << endl;
999 //      OutStream << "No. of times swapped out of main memory      \t: " << ru.ru_nswap << endl;
1000 //      OutStream << "No. of times performed input (read request)  \t: " << ru.ru_inblock << endl;
1001 //      OutStream << "No. of times performed output (write request)\t: " << ru.ru_oublock << endl;
1002 //      OutStream << "No. of signals received                      \t: " << ru.ru_nsignals << endl;
1003       OutStream << "No. of voluntary context switches            \t: " << ru.ru_nvcsw << endl;
1004       OutStream << "No. of involuntary context switches          \t: " << ru.ru_nivcsw << endl;
1005    }
1006    else
1007       OutStream << NA << endl;
1008 #else
1009    OutStream << NA << endl;
1010 #endif
1011 
1012 #ifdef _OPENMP
1013 #pragma omp parallel
1014 {
1015    if (0 == omp_get_thread_num())
1016    {
1017       OutStream << "Number of OpenMP threads                     \t: " << omp_get_num_threads() << endl;
1018       OutStream << "Number of OpenMP processors                  \t: " << omp_get_num_procs() << endl;
1019    }
1020 }
1021 #endif
1022 }
1023 
1024 
1025 /*==============================================================================================================================
1026 
1027  Returns an error message given an error code
1028 
1029 ==============================================================================================================================*/
strGetErrorText(int const nErr)1030 string CDelineation::strGetErrorText(int const nErr)
1031 {
1032    string strErr;
1033 
1034    switch (nErr)
1035    {
1036    case RTN_USERABORT:
1037       strErr = "aborted by user";
1038       break;
1039    case RTN_ERR_BADPARAM:
1040       strErr = "error in command-line parameter";
1041       break;
1042    case RTN_ERR_INI:
1043       strErr = "error reading initialization file";
1044       break;
1045    case RTN_ERR_CLIFFDIR:
1046       strErr = "error in directory name";
1047       break;
1048    case RTN_ERR_RUNDATA:
1049       strErr = "error reading run details file";
1050       break;
1051    case RTN_ERR_LOGFILE:
1052       strErr = "error creating log file";
1053       break;
1054    case RTN_ERR_OUTFILE:
1055       strErr = "error creating text output file";
1056       break;
1057    case RTN_ERR_DEMFILE:
1058       strErr = "error reading initial DEM file";
1059       break;
1060    case RTN_ERR_RASTER_FILE_READ:
1061       strErr = "error reading raster GIS file";
1062       break;
1063    case RTN_ERR_VECTOR_FILE_READ:
1064       strErr = "error reading vector GIS file";
1065       break;
1066    case RTN_ERR_MEMALLOC:
1067       strErr = "error allocating memory";
1068       break;
1069    case RTN_ERR_RASTER_GIS_OUT_FORMAT:
1070       strErr = "problem with raster GIS output format";
1071       break;
1072    case RTN_ERR_VECTOR_GIS_OUT_FORMAT:
1073       strErr = "problem with vector GIS output format";
1074       break;
1075    case RTN_ERR_TEXT_FILE_WRITE:
1076       strErr = "error writing text output file";
1077       break;
1078    case RTN_ERR_RASTER_FILE_WRITE:
1079       strErr = "error writing raster GIS output file";
1080       break;
1081    case RTN_ERR_VECTOR_FILE_WRITE:
1082       strErr = "error writing vector GIS output file";
1083       break;
1084    case RTN_ERR_TIMESERIES_FILE_WRITE:
1085       strErr = "error writing time series output file";
1086       break;
1087    case RTN_ERR_LINETOGRID:
1088       strErr = "error putting linear feature onto raster grid";
1089       break;
1090    case RTN_ERR_NOSEACELLS:
1091       strErr = "no sea cells found";
1092       break;
1093    case RTN_ERR_GRIDTOLINE:
1094       strErr = "error when searching grid for linear feature";
1095       break;
1096    case RTN_ERR_FINDCOAST:
1097       strErr = "error finding coastline on grid";
1098       break;
1099    case RTN_ERR_PROFILEWRITE:
1100       strErr = "error writing coastline-normal profiles";
1101       break;
1102    case RTN_ERR_TIMEUNITS:
1103       strErr = "error in time units";
1104       break;
1105    case RTN_ERR_BADENDPOINT:
1106       strErr = "finding end point for coastline-normal line";
1107       break;
1108    case RTN_ERR_PROFILESPACING:
1109       strErr = "profiles are too closely spaced";
1110       break;
1111    case RTN_ERR_BADPROFILE:
1112       strErr = "could not create profile";
1113       break;
1114    case RTN_ERR_BAD_MULTILINE:
1115       strErr = "inconsistent multiline";
1116       break;
1117    case RTN_ERR_CANNOT_INSERT_POINT:
1118       strErr = "cannot insert point into multiline";
1119       break;
1120    default:
1121       // should never get here
1122       strErr = "unknown error";
1123    }
1124 
1125    return strErr;
1126 }
1127 
1128 
1129 /*==============================================================================================================================
1130 
1131  Notifies the user that the simulation has ended, asks for keypress if necessary, and if compiled under GNU can send an email
1132 
1133 ==============================================================================================================================*/
DoDelineationEnd(int const nRtn)1134 void CDelineation::DoDelineationEnd(int const nRtn)
1135 {
1136    switch (nRtn)
1137    {
1138    case (RTN_OK):
1139       // normal ending
1140       cout << RUNENDNOTICE << ctime(&m_tSysEndTime);
1141       break;
1142 
1143    case (RTN_HELPONLY):
1144    case (RTN_CHECKONLY):
1145       return;
1146 
1147    default:
1148       // Aborting because of some error
1149       time(&m_tSysEndTime);
1150       cerr << ERRORNOTICE << nRtn << " (" << strGetErrorText(nRtn) << ") on " << ctime(&m_tSysEndTime);
1151 
1152       if (LogStream && LogStream.is_open())
1153       {
1154          LogStream << ERR << strGetErrorText(nRtn) << " (error code " << nRtn << ") on " << ctime(&m_tSysEndTime);
1155          LogStream.flush();
1156       }
1157 
1158       if (OutStream && OutStream.is_open())
1159       {
1160          OutStream << ERR << strGetErrorText(nRtn) << " (error code " << nRtn << ") on " << ctime(&m_tSysEndTime);
1161          OutStream.flush();
1162       }
1163    }
1164 
1165 #ifdef __GNUG__
1166    if (isatty(1))
1167    {
1168       // Stdout is connected to a tty, so not running as a background job
1169       cout << endl << PRESSKEY;
1170       cout.flush();
1171       getchar();
1172    }
1173    else
1174    {
1175       // Stdout is not connected to a tty, so must be running in the background; if we have something entered for the email address, then send an email
1176       if (! m_strMailAddress.empty())
1177       {
1178          cout << SENDEMAIL << m_strMailAddress << endl;
1179 
1180          string strCmd("echo \"");
1181          time_t tNow;
1182          time(&tNow);
1183 
1184          // Send an email using Linux/Unix mail command
1185          if (RTN_OK == nRtn)
1186          {
1187             // Finished normally
1188             strCmd.append("Simulation ");
1189             strCmd.append(m_strRunName);
1190             strCmd.append(", running on ");
1191             strCmd.append(strGetComputerName());
1192             strCmd.append(", completed normally on ");
1193             strCmd.append(ctime(&tNow));
1194             strCmd.append("\" | mail -s \"");
1195             strCmd.append(PROGNAME);
1196             strCmd.append(": normal completion\" ");
1197             strCmd.append(m_strMailAddress);
1198          }
1199          else
1200          {
1201             // Error, so give some information to help debugging
1202             strCmd.append("Simulation ");
1203             strCmd.append(m_strRunName);
1204             strCmd.append(", running on ");
1205             strCmd.append(strGetComputerName());
1206             strCmd.append(", aborted with error code ");
1207             char szTmp[15] = "";
1208             pszLongToSz(nRtn, szTmp, 3);
1209             strCmd.append(szTmp);
1210             strCmd.append(": ");
1211             strCmd.append(strGetErrorText(nRtn));
1212 
1213             strCmd.append(").\n\nThis message sent ");
1214             strCmd.append(ctime(&tNow));
1215             strCmd.append("\" | mail -s \"");
1216             strCmd.append(PROGNAME);
1217             strCmd.append(": ERROR\" ");
1218             strCmd.append(m_strMailAddress);
1219          }
1220          int nRet = system(strCmd.c_str());
1221 		 #ifndef WEXITSTATUS	// FreeBSD/clang build
1222 		 #define WEXITSTATUS(x) ((x) & 0xff)
1223 		 #endif
1224 		 if (WEXITSTATUS(nRet) != 0)
1225             cerr << ERR << EMAILERROR << endl;
1226       }
1227    }
1228 #endif
1229 }
1230