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