PetaVision  Alpha
PV_Init.cpp
1 /*
2  * PV_Init.cpp
3  *
4  * Created on: Jul 31, 2015
5  * Author: slundquist
6  */
7 #include "PV_Init.hpp"
8 #include "cMakeHeader.h"
9 #include "columns/CommandLineArguments.hpp"
10 #include "columns/ConfigFileArguments.hpp"
11 #include "columns/HyPerCol.hpp"
12 #include "utils/PVLog.hpp"
13 #include <csignal>
14 #ifdef PV_USE_OPENMP_THREADS
15 #include <omp.h>
16 #endif // PV_USE_OPENMP_THREADS
17 
18 namespace PV {
19 
20 PV_Init::PV_Init(int *argc, char **argv[], bool allowUnrecognizedArguments) {
21  // Initialize MPI
22  initSignalHandler();
23  commInit(argc, argv);
24  initMaxThreads();
25  mArgC = *argc;
26  mArgV.resize(mArgC + 1);
27  for (int a = 0; a < mArgC; a++) {
28  mArgV[a] = argv[0][a];
29  }
30  params = nullptr;
31  mCommunicator = nullptr;
32 
33  // If first argument starts with a non-hyphen character, take it to be a config file.
34  // Otherwise, assume config options are being set on the command line.
35  if (mArgC >= 2 && mArgV[1] != nullptr && mArgV[1][0] != '-') {
36  // Communicator doesn't get set until call to initialize(), which we can't call until rows,
37  // columns, etc.
38  // are set. We therefore need to use MPI_COMM_WORLD as the MPI communicator.
39  arguments = new ConfigFileArguments(
40  std::string{mArgV[1]}, MPI_COMM_WORLD, allowUnrecognizedArguments);
41 
42  // Check if "--require-return" was set.
43  for (int arg = 2; arg < mArgC; arg++) {
44  if (pv_getopt(mArgC, mArgV.data(), "--require-return", nullptr) == 0) {
45  arguments->setBooleanArgument("RequireReturn", true);
46  }
47  }
48  }
49  else {
50  arguments = new CommandLineArguments(mArgC, mArgV.data(), allowUnrecognizedArguments);
51  }
52  initLogFile(false /*appendFlag*/);
53  initialize(); // must be called after initialization of arguments data member.
54 }
55 
57  delete params;
58  delete mCommunicator;
59  delete arguments;
60  commFinalize();
61 }
62 
63 int PV_Init::initSignalHandler() {
64  // Block SIGUSR1. root process checks for SIGUSR1 during advanceTime() and
65  // broadcasts sends to all processes,
66  // which saves the result in the checkpointSignal member variable.
67  // When run() checks whether to call checkpointWrite, it looks at
68  // checkpointSignal, and writes a
69  // checkpoint if checkpointWriteFlag is true, regardless of whether the next
70  // scheduled checkpoint time has arrived.
71  //
72  // This routine must be called before MPI_Initialize; otherwise a thread
73  // created by MPI will not get the signal handler
74  // but will get the signal and the job will terminate.
75  sigset_t blockusr1;
76  sigemptyset(&blockusr1);
77  sigaddset(&blockusr1, SIGUSR1);
78  sigprocmask(SIG_BLOCK, &blockusr1, NULL);
79  return 0;
80 }
81 
83  delete mCommunicator;
84  mCommunicator = new Communicator(arguments);
85  int status = PV_SUCCESS;
86  // It is okay to initialize without there being a params file.
87  // setParams() can be called later.
88  delete params;
89  params = nullptr;
90  std::string paramsFile = arguments->getStringArgument("ParamsFile");
91  if (!paramsFile.empty()) {
92  status = createParams();
93  }
95  return status;
96 }
97 
98 int PV_Init::initMaxThreads() {
99 #ifdef PV_USE_OPENMP_THREADS
100  maxThreads = omp_get_max_threads();
101 #else // PV_USE_OPENMP_THREADS
102  maxThreads = 1;
103 #endif // PV_USE_OPENMP_THREADS
104  return PV_SUCCESS;
105 }
106 
107 int PV_Init::commInit(int *argc, char ***argv) {
108  int mpiInit;
109  // If MPI wasn't initialized, initialize it.
110  // Remember if it was initialized on entry; the destructor will only finalize
111  // if the constructor
112  // init'ed.
113  // This way, you can do several simulations sequentially by initializing MPI
114  // before creating
115  // the first HyPerCol; after running the first simulation the MPI environment
116  // will still exist
117  // and you
118  // can run the second simulation, etc.
119  MPI_Initialized(&mpiInit);
120  if (!mpiInit) {
121  pvAssert((*argv)[*argc] == NULL); // Open MPI 1.7 assumes this.
122  MPI_Init(argc, argv);
123  }
124  else {
125  Fatal() << "PV_Init communicator already initialized\n";
126  }
127 
128  return 0;
129 }
130 
131 void PV_Init::initLogFile(bool appendFlag) {
132  // TODO: Under MPI, non-root processes should send messages to root process.
133  // Currently, if logFile is directory/filename.txt, the root process writes to
134  // that path,
135  // and nonroot processes write to directory/filename_<rank>.txt, where <rank>
136  // is replaced with
137  // the global rank.
138  // If filename does not have an extension, _<rank> is appended.
139  // Note that the global rank zero process does not insert _<rank>. This is
140  // deliberate, as the
141  // nonzero ranks
142  // should be MPI-ing the data to the zero rank.
143  std::string logFile = arguments->getStringArgument("LogFile");
144  int const globalRootProcess = 0;
145  int globalRank;
146  MPI_Comm_rank(MPI_COMM_WORLD, &globalRank);
147  std::ios_base::openmode mode =
148  appendFlag ? std::ios_base::out | std::ios_base::app : std::ios_base::out;
149  if (!logFile.empty() && globalRank != globalRootProcess) {
150  // To prevent collisions caused by multiple processes opening the same file
151  // for logging,
152  // processes with global rank other than zero append the rank to the log
153  // filename.
154  // If the logfile has an extension (e.g. ".log", ".txt"), the rank is
155  // appended before the
156  // period separating the extension.
157  std::string logFileString(logFile);
158  size_t finalSlash = logFileString.rfind('/');
159  size_t finalDot = logFileString.rfind('.');
160  size_t insertionPoint;
161  if (finalDot == std::string::npos
162  || (finalSlash != std::string::npos && finalDot < finalSlash)) {
163  insertionPoint = logFileString.length();
164  }
165  else {
166  insertionPoint = finalDot;
167  }
168  std::string insertion("_");
169  insertion.append(std::to_string(globalRank));
170  logFileString.insert(insertionPoint, insertion);
171  PV::setLogFile(logFileString, mode);
172  }
173  else {
174  PV::setLogFile(logFile, mode);
175  }
176 }
177 
178 int PV_Init::setParams(char const *params_file) {
179  if (params_file == nullptr) {
180  return PV_FAILURE;
181  }
182  arguments->setStringArgument("ParamsFile", std::string{params_file});
183  initialize();
184  return createParams();
185 }
186 
188  std::string paramsFile = arguments->getStringArgument("ParamsFile");
189  if (!paramsFile.empty()) {
190  delete params;
191  params = new PVParams(
192  paramsFile.c_str(),
193  2 * (INITIAL_LAYER_ARRAY_SIZE + INITIAL_CONNECTION_ARRAY_SIZE),
194  mCommunicator);
195  return PV_SUCCESS;
196  }
197  else {
198  return PV_FAILURE;
199  }
200 }
201 
202 int PV_Init::setLogFile(char const *logFile, bool appendFlag) {
203  std::string logFileString{logFile};
204  arguments->setStringArgument("LogFile", logFileString);
205  initLogFile(appendFlag);
207  return PV_SUCCESS;
208 }
209 
210 int PV_Init::setMPIConfiguration(int rows, int columns, int batchWidth) {
211  if (rows >= 0) {
212  arguments->setIntegerArgument("NumRows", rows);
213  }
214  if (columns >= 0) {
215  arguments->setIntegerArgument("NumColumns", columns);
216  }
217  if (batchWidth >= 0) {
218  arguments->setIntegerArgument("BatchWidth", batchWidth);
219  }
220  initialize();
221  return PV_SUCCESS;
222 }
223 
225  Communicator *communicator = getCommunicator();
226  if (communicator == nullptr or communicator->globalCommRank() == 0) {
227  time_t currentTime = time(nullptr);
228  InfoLog() << "PetaVision initialized at "
229  << ctime(&currentTime); // string returned by ctime contains a trailing \n.
230  InfoLog() << "Configuration is:\n";
231  printState();
232  InfoLog().printf("----------------\n");
233  }
234 }
235 
237  arguments->resetState();
238  return PV_SUCCESS;
239 }
240 
241 int PV_Init::registerKeyword(char const *keyword, ObjectCreateFn creator) {
242  int status = Factory::instance()->registerKeyword(keyword, creator);
243  if (status != PV_SUCCESS) {
244  if (getWorldRank() == 0) {
245  ErrorLog().printf("PV_Init: keyword \"%s\" has already been registered.\n", keyword);
246  }
247  }
248  return status;
249 }
250 
251 char **PV_Init::getArgsCopy() const {
252  char **argumentArray = (char **)pvMallocError(
253  (size_t)(mArgC + 1) * sizeof(char *),
254  "PV_Init::getArgsCopy allocate memory for %d arguments: %s\n",
255  mArgC,
256  strerror(errno));
257  for (int a = 0; a < mArgC; a++) {
258  char const *arga = mArgV[a];
259  if (arga) {
260  char *copied = strdup(arga);
261  if (!copied) {
262  ErrorLog().printf("PV_Init unable to store argument %d: %s\n", a, strerror(errno));
263  Fatal().printf("Argument was \"%s\".\n", arga);
264  }
265  argumentArray[a] = copied;
266  }
267  else {
268  argumentArray[a] = nullptr;
269  }
270  }
271  argumentArray[mArgC] = nullptr;
272  return argumentArray;
273 }
274 
275 void PV_Init::freeArgs(int argc, char **argv) {
276  for (int k = 0; k < argc; k++) {
277  free(argv[k]);
278  }
279  free(argv);
280 }
281 
282 int PV_Init::commFinalize() {
283  MPI_Finalize();
284  return 0;
285 }
286 
287 } // namespace PV
int setLogFile(char const *val, bool appendFlag=false)
Definition: PV_Init.cpp:202
bool setBooleanArgument(std::string const &name, bool const &value)
Definition: Arguments.hpp:107
static void freeArgs(int argc, char **argv)
Definition: PV_Init.cpp:275
bool setStringArgument(std::string const &name, std::string const &value)
Definition: Arguments.hpp:134
std::string const & getStringArgument(std::string const &name) const
Definition: Arguments.hpp:89
void resetState(std::istream &configStream, bool allowUnrecognizedArguments)
Definition: Arguments.cpp:32
int resetState()
Definition: PV_Init.cpp:236
PV_Init(int *argc, char **argv[], bool allowUnrecognizedArguments)
Definition: PV_Init.cpp:20
void printState() const
Definition: PV_Init.hpp:121
bool setIntegerArgument(std::string const &name, int const &value)
Definition: Arguments.hpp:116
int createParams()
Definition: PV_Init.cpp:187
void initLogFile(bool appendFlag)
Definition: PV_Init.cpp:131
char ** getArgsCopy() const
Definition: PV_Init.cpp:251
int setParams(char const *paramsFile)
Definition: PV_Init.cpp:178
int initialize()
Definition: PV_Init.cpp:82
int registerKeyword(char const *keyword, ObjectCreateFn creator)
Definition: PV_Init.cpp:241
virtual ~PV_Init()
Definition: PV_Init.cpp:56
void printInitMessage()
Definition: PV_Init.cpp:224
int setMPIConfiguration(int rows, int columns, int batchwidth)
Definition: PV_Init.cpp:210