MueLu  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MueLu_Utilities.cpp
Go to the documentation of this file.
1 // @HEADER
2 // *****************************************************************************
3 // MueLu: A package for multigrid based preconditioning
4 //
5 // Copyright 2012 NTESS and the MueLu contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
10 #include "MueLu_Utilities_def.hpp"
11 
12 #include <string>
13 #include <locale>
14 #ifdef _WIN32
15 #define NOMINMAX
16 #endif
17 #include <algorithm>
18 
19 #ifdef HAVE_MUELU_EPETRAEXT
21 #endif
22 
23 #ifdef HAVE_MPI
24 #include <mpi.h>
25 #ifdef _WIN32
26 #include <winsock2.h>
27 #else
28 #include <netdb.h>
29 #include <arpa/inet.h>
30 #endif
31 #endif
32 
33 namespace MueLu {
34 
37 
38  long maxLevel = 0;
39 
40  for (ParameterList::ConstIterator inListEntry = inList.begin(); inListEntry != inList.end(); inListEntry++) {
41  const std::string& levelName = inListEntry->first;
42 
43  // Check for match of the form "level X" where X is a positive integer
44  if (inList.isSublist(levelName) && ((levelName.find("level ") == 0 && levelName.size() > 6) || levelName.find("user data") == 0)) {
45  int levelID = strtol(levelName.substr(6).c_str(), 0, 0);
46  bool userFlag = true;
47  if (levelName.find("user data") == std::string::npos) { // if "user data" is not found in levelName, switch userFlag and set levelID
48  userFlag = false;
49  levelID = strtol(levelName.substr(6).c_str(), 0, 0);
50  if (maxLevel < levelID)
51  maxLevel = levelID;
52  }
53 
54  // Split the sublist
55  const ParameterList& levelList = inList.sublist(levelName);
56  for (ParameterList::ConstIterator levelListEntry = levelList.begin(); levelListEntry != levelList.end(); levelListEntry++) {
57  const std::string& name = levelListEntry->first;
58  // Check if the name starts with "Nullspace", has length > 9, and the last character is a digit
59  bool isNumberedNullspace = (name.rfind("Nullspace", 0) == 0 && name.length() > 9 && std::isdigit(name.back(), std::locale::classic()));
60 
61  if (name == "A" || name == "P" || name == "R" || name == "M" || name == "Mdiag" || name == "K" || name == "Nullspace" || name == "Material" || name == "Coordinates" || name == "D0" || name == "Dk_1" || name == "Dk_2" || name == "Mk_one" || name == "Mk_1_one" || name == "M1_beta" || name == "M1_alpha" || name == "invMk_1_invBeta" || name == "invMk_2_invAlpha" || name == "M1" || name == "Ms" || name == "M0inv" || name == "Pnodal" || name == "NodeMatrix" || name == "NodeAggMatrix" || name == "Node Comm" || name == "DualNodeID2PrimalNodeID"
62 #ifdef HAVE_MUELU_INTREPID2 // For the IntrepidPCoarsenFactory
63  || name == "pcoarsen: element to node map"
64 #endif
65  || name == "output stream" || isNumberedNullspace) {
66  nonSerialList.sublist(levelName).setEntry(name, levelListEntry->second);
67  }
68 #ifdef HAVE_MUELU_MATLAB
69  else if (!userFlag && IsParamMuemexVariable(name)) {
70  nonSerialList.sublist(levelName).setEntry(name, levelListEntry->second);
71  }
72 #endif
73  else if (userFlag && IsParamValidVariable(name)) {
74  nonSerialList.sublist(levelName).setEntry(name, levelListEntry->second);
75  } else {
76  serialList.sublist(levelName).setEntry(name, levelListEntry->second);
77  }
78  }
79 
80  } else {
81  serialList.setEntry(inListEntry->first, inListEntry->second);
82  }
83  }
84 
85  return maxLevel;
86 }
87 
88 void TokenizeStringAndStripWhiteSpace(const std::string& stream, std::vector<std::string>& tokenList, const char* delimChars) {
89  // note: default delimiter string is ","
90  // Take a comma-separated list and tokenize it, stripping out leading & trailing whitespace. Then add to tokenList
91  char* buf = (char*)malloc(stream.size() + 1);
92  strcpy(buf, stream.c_str());
93  char* token = strtok(buf, delimChars);
94  if (token == NULL) {
95  free(buf);
96  return;
97  }
98  while (token) {
99  // token points to start of string to add to tokenList
100  // remove front whitespace...
101  char* tokStart = token;
102  char* tokEnd = token + strlen(token) - 1;
103  while (*tokStart == ' ' && tokStart < tokEnd)
104  tokStart++;
105  while (*tokEnd == ' ' && tokStart < tokEnd)
106  tokEnd--;
107  tokEnd++;
108  if (tokStart < tokEnd) {
109  std::string finishedToken(tokStart, tokEnd - tokStart); // use the constructor that takes a certain # of chars
110  tokenList.push_back(finishedToken);
111  }
112  token = strtok(NULL, delimChars);
113  }
114  free(buf);
115 }
116 
117 bool IsParamMuemexVariable(const std::string& name) {
118  // see if paramName is exactly two "words" - like "OrdinalVector myNullspace" or something
119  char* str = (char*)malloc(name.length() + 1);
120  strcpy(str, name.c_str());
121  // Strip leading and trailing whitespace
122  char* firstWord = strtok(str, " ");
123  if (!firstWord) {
124  free(str);
125  return false;
126  }
127  char* secondWord = strtok(NULL, " ");
128  if (!secondWord) {
129  free(str);
130  return false;
131  }
132  char* thirdWord = strtok(NULL, " ");
133  if (thirdWord) {
134  free(str);
135  return false;
136  }
137  // convert first word to all lowercase for case insensitive compare
138  char* tolowerIt = firstWord;
139  while (*tolowerIt) {
140  *tolowerIt = (char)tolower(*tolowerIt);
141  tolowerIt++;
142  }
143  // See if the first word is one of the custom variable names
144  if (strstr(firstWord, "matrix") ||
145  strstr(firstWord, "multivector") ||
146  strstr(firstWord, "map") ||
147  strstr(firstWord, "ordinalvector") ||
148  strstr(firstWord, "int") ||
149  strstr(firstWord, "scalar") ||
150  strstr(firstWord, "double") ||
151  strstr(firstWord, "complex") ||
152  strstr(firstWord, "string"))
153  // Add name to list of keys to remove
154  {
155  free(str);
156  return true;
157  } else {
158  free(str);
159  return false;
160  }
161 }
162 
163 bool IsParamValidVariable(const std::string& name) {
164  // see if paramName is exactly two "words" - like "OrdinalVector myNullspace" or something
165  char* str = (char*)malloc(name.length() + 1);
166  strcpy(str, name.c_str());
167  // Strip leading and trailing whitespace
168  char* firstWord = strtok(str, " ");
169  if (!firstWord) {
170  free(str);
171  return false;
172  }
173  char* secondWord = strtok(NULL, " ");
174  if (!secondWord) {
175  free(str);
176  return false;
177  }
178  char* thirdWord = strtok(NULL, " ");
179  if (thirdWord) {
180  free(str);
181  return false;
182  }
183  // convert first word to all lowercase for case insensitive compare
184  char* tolowerIt = firstWord;
185  while (*tolowerIt) {
186  *tolowerIt = (char)tolower(*tolowerIt);
187  tolowerIt++;
188  }
189  // See if the first word is one of the custom variable names
190  if (strstr(firstWord, "matrix") ||
191  strstr(firstWord, "multivector") ||
192  strstr(firstWord, "map") ||
193  strstr(firstWord, "ordinalvector") ||
194  strstr(firstWord, "int") ||
195  strstr(firstWord, "scalar") ||
196  strstr(firstWord, "double") ||
197  strstr(firstWord, "complex") ||
198  strstr(firstWord, "string") ||
199  strstr(firstWord, "array<go>") ||
200  strstr(firstWord, "array<lo>") ||
201  strstr(firstWord, "arrayrcp<lo>") ||
202  strstr(firstWord, "arrayrcp<go>"))
203  // Add name to list of keys to remove
204  {
205  free(str);
206  return true;
207  } else {
208  free(str);
209  return false;
210  }
211 }
212 
213 // Generates a communicator whose only members are other ranks of the baseComm on my node
214 Teuchos::RCP<const Teuchos::Comm<int> > GenerateNodeComm(RCP<const Teuchos::Comm<int> >& baseComm, int& NodeId, const int reductionFactor) {
215 #ifdef HAVE_MPI
216  int numRanks = baseComm->getSize();
217  if (numRanks == 1) {
218  NodeId = baseComm->getRank();
219  return baseComm;
220  }
221 
222  // Get an integer from the hostname
223  char hostname[MPI_MAX_PROCESSOR_NAME];
224  int len;
225  MPI_Get_processor_name(hostname, &len);
226  struct hostent* host = gethostbyname(hostname);
227  int myaddr = (int)inet_addr(inet_ntoa(*(struct in_addr*)host->h_addr));
228 
229  // All-to-all exchange of address integers
230  std::vector<int> addressList(numRanks);
231  Teuchos::gatherAll(*baseComm, 1, &myaddr, numRanks, &addressList[0]);
232 
233  // Sort!
234  std::sort(addressList.begin(), addressList.end());
235 
236  // Find which node I'm on (and stop when I've done that)
237  int numNodes = 0;
238  for (int i = 0, prev = addressList[0]; i < numRanks && prev != myaddr; i++) {
239  if (prev != addressList[i]) {
240  prev = addressList[i];
241  numNodes++;
242  }
243  }
244  NodeId = numNodes;
245 
246  // Generate nodal communicator
247  Teuchos::RCP<const Teuchos::Comm<int> > newComm = baseComm->split(NodeId, baseComm->getRank());
248 
249  // If we want to divide nodes up (for really beefy nodes), we do so here
250  if (reductionFactor != 1) {
251  // Find # cores per node
252  int lastI = 0;
253  int coresPerNode = 0;
254  for (int i = 0, prev = addressList[0]; i < numRanks; i++) {
255  if (prev != addressList[i]) {
256  prev = addressList[i];
257  coresPerNode = std::max(i - lastI, coresPerNode);
258  lastI = i;
259  }
260  }
261  coresPerNode = std::max(numRanks - lastI, coresPerNode);
262 
263  // Can we chop that up?
264  if (coresPerNode % reductionFactor != 0)
265  throw std::runtime_error("Reduction factor does not evently divide # cores per node");
266  int reducedCPN = coresPerNode / reductionFactor;
267  int nodeDivision = newComm->getRank() / reducedCPN;
268 
269  NodeId = numNodes * reductionFactor + nodeDivision;
270  newComm = baseComm->split(NodeId, baseComm->getRank());
271  }
272 
273  return newComm;
274 #else
275  NodeId = baseComm->getRank();
276  return baseComm;
277 #endif
278 }
279 
280 } // namespace MueLu
ParameterList & setEntry(const std::string &name, U &&entry)
ConstIterator end() const
std::string tolower(const std::string &str)
bool IsParamMuemexVariable(const std::string &name)
bool isSublist(const std::string &name) const
params_t::ConstIterator ConstIterator
ConstIterator begin() const
bool IsParamValidVariable(const std::string &name)
ParameterList & sublist(const std::string &name, bool mustAlreadyExist=false, const std::string &docString="")
void TokenizeStringAndStripWhiteSpace(const std::string &stream, std::vector< std::string > &tokenList, const char *delimChars)
Teuchos::RCP< const Teuchos::Comm< int > > GenerateNodeComm(RCP< const Teuchos::Comm< int > > &baseComm, int &NodeId, const int reductionFactor)
long ExtractNonSerializableData(const Teuchos::ParameterList &inList, Teuchos::ParameterList &serialList, Teuchos::ParameterList &nonSerialList)
Extract non-serializable data from level-specific sublists and move it to a separate parameter list...