simplifyGDML.cc
Go to the documentation of this file.
1 /**
2  * @file simplifyGDML.cc
3  * @brief Reprocesses a GDML file via GEANT4.
4  * @author Gianluca Petrillo (petrillo@fnal.gov)
5  * @date June 13, 2017
6  *
7  * Run with `--help` argument for usage instructions.
8  *
9  * This program needs to be linked to:
10  * - GEANT4
11  * - CLHEP
12  * - XERCES (C)
13  *
14  * Example of build command in UPS environment:
15  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16  * g++ -Wall -pedantic -std=c++14 \
17  * -I"${GEANT4_FQ_DIR}/include" -I"$CLHEP_INC" -I"$XERCES_C_INC" \
18  * -L"${GEANT4_FQ_DIR}/lib64" -lG4persistency -lG4geometry \
19  * -L"$XERCES_C_LIB" -lxerces-c \
20  * -o simplifyGDML.exe simplifyGDML.cc
21  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
22  *
23  * Note that this program is a glorified 3-line code: parse XML file, get
24  * the world volume, write it.
25  *
26  */
27 
28 // GEANT4
29 #include "Geant4/G4GDMLParser.hh"
30 
31 // POSIX/UNIX
32 #include <getopt.h> // getopt_long(), option
33 #include <unistd.h> // access()
34 
35 // C/C++ standard libraries
36 #include <string>
37 #include <iostream>
38 #include <cstdio> // std::remove()
39 #include <cstdlib> // std::exit()
40 
41 //------------------------------------------------------------------------------
42 // argument parsing
44 
45  static const unsigned int StartingDebugLevel = 0;
48 
49  std::string sourcePath; ///< GDML input file name.
50  std::string destPath; ///< GDML output file name.
51 
52  /// Name of the chosen setup in the GDML source.
54 
55  /// URL of the schema used while writing GDML.
57 
58  bool validate = false; ///< Ask Geant4 to validate the source.
59  bool overwrite = false; ///< Overwrite the output file if already present.
60  bool dontWrite = false; ///< Do not write the output file.
61  bool help = false; ///< Print usage instructions and exit.
62  unsigned int debugLevel = StartingDebugLevel; ///< Debug level.
63 
64  void print() const;
65 
66 }; // struct ConfigurationParameters
67 
70  = G4GDML_DEFAULT_SCHEMALOCATION;
71 
73 
74  std::cout << "Configuration:"
75  "\n input file: '" << sourcePath << "'"
76  "\n output file: '" << destPath << "'"
77  "\n setup name: '" << setupName << "'"
78  "\n schema path: '" << schemaPath << "'"
79  "\n validate: " << std::boolalpha << validate <<
80  "\n only read: " << std::boolalpha << dontWrite <<
81  "\n overwrite: " << std::boolalpha << overwrite <<
82  "\n print help: " << std::boolalpha << help <<
83  "\n debug level: " << debugLevel <<
84  std::endl;
85 
86 } // ConfigurationParameters::print()
87 
88 
89 
91 
92  static const unsigned int DefaultDebugLevel = 1;
93 
94  typedef enum {
100  MissingArgument
101  } error;
102 
103  static error parse
104  (ConfigurationParameters& params, unsigned int argc, char** argv);
105 
106  static void printHelp
107  (int exitCode = 0, const char* progName = "simplifyGDML");
108 
109  private:
110  struct opt; // single letter options namespace
111 
112 }; // struct ConfigurationParameters
113 
114 
116  (int exitCode /* = 0 */, const char* progName /* = "simplifyGDML" */)
117 {
118  std::cout <<
119  "Asks GEANT4 to process and rewrite a GDML file."
120  " This effectively simplifies the complexity of constructs in the GDML"
121  " code."
122  "\n"
123  "\nUsage: " << progName << " [options] [--] inputPath [outputPath]"
124  "\n"
125  "\nThe output file will contain the world volume of the specified setup."
126  "\nIt must be different from the input file:"
127  " GEANT4 will refuse to overwrite."
128  "\n"
129  "\nNOTE: the path to the GDML schema in the output may need to be fixed by"
130  " hand."
131  "\nTo allow validation, the GDML schema must be present as described in the"
132  " header of the GDML file."
133  "\nLArSoft does not necessarily distributes GDML schema, so these lines"
134  " are aimed to load them from network:"
135  "\n"
136  "\n<gdml xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
137  "\n xsi:noNamespaceSchemaLocation=\"http://service-spi.web.cern.ch/service-spi/app/releases/GDML/schema/gdml.xsd\""
138  "\n >"
139  "\n"
140  "\nOptions:"
141  "\n--validate , -V"
142  "\n ask Geant4 to validated the GDML while reading (validation output"
143  "\n will be on screen, with no effect to the rest of the program)"
144  "\n--overwrite , -f"
145  "\n not implemented yet"
146  "\n--nowrite , -r"
147  "\n only read (and validate as requested), do not write a new GDML file"
148  "\n--setup=SETUPNAME , -s SETUPNAME"
149  "\n name of path or URL to the schema to use for writing."
150  "\n By default, the '" << ConfigurationParameters::DefaultSetupName
151  << "' setup is chosen."
152  "\n--schema=SCHEMAURL , -S SCHEMAURL"
153  "\n path or URL to the schema to use for writing."
154  "\n By default, the '" << ConfigurationParameters::DefaultSchemaURL
155  << "' schema is used."
156  "\n--help , -h , -?"
157  "\n print these usage instructions and exit"
158  "\n"
159  << std::endl;
160  if (exitCode >= 0) std::exit(exitCode);
161 } // ConfigurationParser::printHelp()
162 
163 
165  static constexpr const char Validate = 'v';
166  static constexpr const char Schema = 'S';
167  static constexpr const char Setup = 's';
168  static constexpr const char NoWrite = 'r';
169  static constexpr const char Overwrite = 'f';
170  static constexpr const char Debug = 'd';
171  static constexpr const char Help = 'h';
172 }; // ConfigurationParser::opt
173 
175  (ConfigurationParameters& params, unsigned int argc, char** argv)
176 {
177  static const option longopts[] = {
178  { "validate", no_argument, NULL, opt::Validate },
179  { "schema", required_argument, NULL, opt::Schema },
180  { "setup", required_argument, NULL, opt::Setup },
181  { "overwrite", no_argument, NULL, opt::Overwrite },
182  { "nowrite", no_argument, NULL, opt::NoWrite },
183  { "debug", optional_argument, NULL, opt::Debug },
184  { "help", no_argument, NULL, opt::Help },
185 // { "one", no_argument, &value, 1 },
186 // { "two", no_argument, &value, 2 },
187  { NULL, 0, NULL, 0 }
188  }; // longopts
189 
190  // automatically build the short option string from the long one
191  std::string shortopts = ":"; // this means no error printout
192  for (auto const& longopt: longopts) {
193  if (!longopt.name) break;
194  if (longopt.val == 0) continue;
195  shortopts += (char) longopt.val;
196  if (longopt.has_arg != no_argument) shortopts += ':';
197  if (longopt.has_arg == optional_argument) shortopts += ':';
198  } // for
199 
200  // ----------------------------------------------------------------------
201  // options
202  error res = error::Success;
203  char ch;
204  optind = 1;
205  bool forgiving = false;
206  while
207  ((ch = getopt_long(argc, argv, shortopts.c_str(), longopts, NULL)) != -1)
208  {
209  switch (ch) {
210  case opt::Validate:
211  params.validate = true;
212  continue;
213  case opt::Setup:
214  params.setupName = optarg;
215  continue;
216  case opt::Schema:
217  params.schemaPath = optarg;
218  continue;
219  case opt::Overwrite:
220  params.overwrite = true;
221  continue;
222  case opt::NoWrite:
223  params.dontWrite = true;
224  continue;
225  case opt::Debug:
226  if (optarg) {
227  std::istringstream sstr(optarg);
228  sstr >> params.debugLevel;
229  if (!sstr) {
230  std::cerr << "Invalid debug level: '" << optarg << "'" << std::endl;
231  res = error::InvalidNumber;
232  continue;
233  }
234  }
235  else params.debugLevel = DefaultDebugLevel;
236  continue;
237  case '?': // this is special since has a specific meaning for getopt()
238  if (optopt != '?') {
239  if (!forgiving) {
240  std::cerr
241  << "Invalid option: '" << argv[optind-1] << "'" << std::endl;
242  }
243  res = error::InvalidOption;
244  continue;
245  }
246  // follow into:
247  case opt::Help:
248  params.help = true;
249  forgiving = true;
250  continue;
251  default:
252  std::cerr << "Internal error: option '" << ch << "' not supported yet."
253  << std::endl;
254  res = error::LogicError;
255  continue;
256  } // switch
257  } // while
258 
259  // ----------------------------------------------------------------------
260  // arguments
261  if (optind >= (int) argc) {
262  if (!forgiving) std::cerr << "Source file name is required!" << std::endl;
263  return error::MissingArgument;
264  }
265  params.sourcePath = argv[optind++];
266  if (optind < (int) argc) params.destPath = argv[optind++];
267 
268  if (optind < (int) argc) {
269  if (!forgiving) {
270  std::cerr << "Spurious arguments: '" << argv[optind] << "'";
271  if (optind + 1 < (int) argc)
272  std::cerr << " and " << (argc - optind - 1) << " more";
273  std::cerr << "." << std::endl;
274  }
275  return error::ExtraArguments;
276  }
277 
278  // ----------------------------------------------------------------------
279  return res;
280 } // ConfigurationParser::parse()
281 
282 //------------------------------------------------------------------------------
284  auto const iExt = name.find(".gdml");
285  if (iExt == std::string::npos) name.append(suffix);
286  else name.insert(iExt, suffix);
287  return name;
288 } // addNameSuffix()
289 
290 bool exists(std::string path) {
291  return access(path.c_str(), F_OK);
292 } // exists()
293 
294 //------------------------------------------------------------------------------
295 G4VPhysicalVolume* readWorldVolume
296  (G4GDMLParser& parser, ConfigurationParameters const& params)
297 {
298  decltype(auto) worldVolume = parser.GetWorldVolume(params.setupName);
299  if (!worldVolume) {
300  std::cerr << "Failed to find the world volume for setup '"
301  << params.setupName << "'" << std::endl;
302  }
303  return worldVolume;
304 } // readWorldVolume()
305 
306 int writeWorld(G4GDMLParser& parser, ConfigurationParameters const& params) {
307 
308  std::cout << std::string(80, '-')
309  << "\nFetching the world volume for setup '" << params.setupName << "'..."
310  << std::endl;
311  decltype(auto) worldVolume = readWorldVolume(parser, params);
312  if (!worldVolume) {
313  std::cerr << "Failed to find the world volume for setup '"
314  << params.setupName << "'" << std::endl;
315  return 1;
316  }
317 
318  if (exists(params.destPath)) {
319  if (params.overwrite) {
320  std::cout << "Overwriting the destination file..." << std::endl;
321  std::remove(params.destPath.c_str());
322  }
323  else {
324  std::cerr << "Destination file '" << params.destPath
325  << "' already exists." << std::endl;
326  return 2;
327  }
328  }
329 
330  std::cout << std::string(80, '-')
331  << "\nWriting '" << params.destPath << "'..." << std::endl;
332  parser.Write
333  (params.destPath, worldVolume, false /* do not add references to names*/);
334 
335  std::cout << std::string(80, '-')
336  << "\n'" << params.sourcePath << "' => [" << params.setupName << "] => '"
337  << params.destPath << "': done."
338  << std::endl;
339 
340  return 0;
341 } // writeVolume()
342 
343 
344 //------------------------------------------------------------------------------
345 int main(int argc, char** argv) {
346 
347  //
348  // argument parsing
349  //
351 
352  auto parseRes = ConfigurationParser::parse(params, argc, argv);
353 
354  if (params.destPath.empty() && !params.dontWrite)
355  params.destPath = addNameSuffix(params.sourcePath, "-simplified");
356 
357  if (params.debugLevel > 0) params.print();
358 
359  // print usage instructions before exiting
360  if (params.help) {
361  ConfigurationParser::printHelp(0, argv[0]);
362  return 0;
363  }
364  if (parseRes != ConfigurationParser::Success) return (int) parseRes;
365 
366  //
367  // the magic
368  //
369  G4GDMLParser parser;
370 
371  std::cout << std::string(80, '-')
372  << "\nReading '" << params.sourcePath << "'..." << std::endl;
373  parser.Read(params.sourcePath, params.validate);
374 
375  std::cout << std::string(80, '-')
376  << "\nFetching the world volume for setup '" << params.setupName << "'..."
377  << std::endl;
378  // for c2: worldVolume is unused here
379  //decltype(auto) worldVolume = parser.GetWorldVolume(params.setupName);
380  parser.GetWorldVolume(params.setupName);
381 
382  if (params.dontWrite) {
383  if (!params.destPath.empty()) {
384  std::cerr << "Output path ignored since we don't write anything."
385  << std::endl;
386  }
387  }
388  else {
389  int res = writeWorld(parser, params);
390  if (res != 0) return res;
391  }
392 
393  //
394  // the missing error code
395  //
396  return 0;
397 } // main()
static QCString name
Definition: declinfo.cpp:673
std::string setupName
Name of the chosen setup in the GDML source.
Definition: simplifyGDML.cc:53
std::string string
Definition: nybbler.cc:12
static std::string const DefaultSetupName
Definition: simplifyGDML.cc:46
int main(int argc, char **argv)
bool overwrite
Overwrite the output file if already present.
Definition: simplifyGDML.cc:59
error
Definition: include.cc:26
bool exists(std::string path)
bool help
Print usage instructions and exit.
Definition: simplifyGDML.cc:61
std::string destPath
GDML output file name.
Definition: simplifyGDML.cc:50
bool validate
Ask Geant4 to validate the source.
Definition: simplifyGDML.cc:58
static std::string const DefaultSchemaURL
Definition: simplifyGDML.cc:47
std::string sourcePath
GDML input file name.
Definition: simplifyGDML.cc:49
static void printHelp(int exitCode=0, const char *progName="simplifyGDML")
static error parse(ConfigurationParameters &params, unsigned int argc, char **argv)
G4VPhysicalVolume * readWorldVolume(G4GDMLParser &parser, ConfigurationParameters const &params)
std::string addNameSuffix(std::string name, std::string suffix)
std::string schemaPath
URL of the schema used while writing GDML.
Definition: simplifyGDML.cc:56
bool dontWrite
Do not write the output file.
Definition: simplifyGDML.cc:60
Definition: debug.h:23
unsigned int debugLevel
Debug level.
Definition: simplifyGDML.cc:62
def access(path, mode)
static const unsigned int StartingDebugLevel
Definition: simplifyGDML.cc:45
if(!yymsg) yymsg
int writeWorld(G4GDMLParser &parser, ConfigurationParameters const &params)
QTextStream & endl(QTextStream &s)