OpenMD 3.0
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
atom2omd.cpp
1/**********************************************************************
2atom2md.cpp - OpenBabel-based conversion program to OpenMD file,
3 command-line handling.
4
5Copyright (C) 1998-2001 by OpenEye Scientific Software, Inc.
6Some portions Copyright (C) 2001-2006 by Geoffrey R. Hutchison
7Some portions Copyright (C) 2004-2006 by Chris Morley
8Some portions Copyright (C) 2004-present by J. Daniel Gezelter
9
10This file is part of both the OpenMD and Open Babel projects.
11For more information, see <http://openmd.net> and
12<http://openbabel.sourceforge.net/>
13
14This program is free software; you can redistribute it and/or modify
15it under the terms of the GNU General Public License as published by
16the Free Software Foundation version 2 of the License.
17
18This program is distributed in the hope that it will be useful,
19but WITHOUT ANY WARRANTY; without even the implied warranty of
20MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21GNU General Public License for more details.
22***********************************************************************/
23
24#include <config.h>
25
26// used to set import/export for Cygwin DLLs
27#ifdef WIN32
28#define USING_OBDLL
29#endif
30
31#include <fstream>
32#include <iostream>
33#include <map>
34#include <sstream>
35#include <string>
36
37#include <openbabel/babelconfig.h>
38#if HAVE_CONIO_H
39#include <conio.h>
40#endif
41#include <cstdlib> // for exit() on Linux
42
43#ifdef _MSC_VER
44#define strncasecmp _strnicmp
45#endif
46
47#include <cstring>
48
49#include <openbabel/obconversion.h>
50#include <openbabel/plugin.h>
51
52using namespace std;
53using namespace OpenBabel;
54
55void DoOption(const char* p, OBConversion& Conv, OBConversion::Option_type typ,
56 int& arg, int argc, char* argv[]);
57void usage();
58void help();
59
60// There isn't a great way to do this -- we need to save argv[0] for usage()
61static char* program_name;
62
63int main(int argc, char* argv[]) {
64 OBConversion Conv(&cin, &cout); // default input and output are console
65
66 OBFormat* pInFormat = nullptr;
67 OBFormat* pOutFormat = nullptr;
68 bool outGzip = false;
69 vector<string> FileList, OutputFileList;
70 string OutputFileName;
71
72 // Parse commandline
73 bool gotInType = false, gotOutType = false;
74 bool SplitOrBatch = false;
75
76 char* oext = nullptr;
77 char* iext = nullptr;
78
79 // for use with command name to type conversion
80 string inputExt;
81 string outputExt;
82
83 // Save name of program without its path (and .exe)
84 string pn(argv[0]);
85 string::size_type pos;
86#ifdef _WIN32
87 pos = pn.find(".exe");
88 if (pos != string::npos) argv[0][pos] = '\0';
89#endif
90 pos = pn.find_last_of("/\\");
91 if (pos == string::npos)
92 program_name = argv[0];
93 else
94 program_name = argv[0] + pos + 1;
95
96 const char* p;
97 int arg;
98 for (arg = 1; arg < argc; ++arg) {
99 if (argv[arg]) {
100 if (argv[arg][0] == '-') {
101 char opchar[2] = "?";
102 opchar[0] = argv[arg][1];
103 switch (opchar[0]) {
104 case 'V': {
105 cout << program_name << ": part of OpenMD " << OPENMD_VERSION_MAJOR
106 << "." << OPENMD_VERSION_MINOR << "." << OPENMD_VERSION_TINY
107 << " and Open Babel " << BABEL_VERSION << " -- " << __DATE__
108 << " -- " << __TIME__ << endl;
109 exit(0);
110 }
111
112 case 'i':
113 gotInType = true;
114 iext = argv[arg] + 2;
115 if (!*iext)
116 iext = argv[++arg]; // space left after -i: use next argument
117
118 if (strncasecmp(iext, "MIME", 4) == 0) {
119 // get the MIME type from the next argument
120 iext = argv[++arg];
121 pInFormat = Conv.FormatFromMIME(iext);
122 } else {
123 // The ID provided by the OBFormat class is used as the
124 // identifying file extension
125 pInFormat = Conv.FindFormat(iext);
126 }
127 if (pInFormat == NULL) {
128 cerr << program_name << ": cannot read input format!" << endl;
129 usage();
130 exit(1);
131 }
132 break;
133
134 case 'o':
135 gotOutType = true;
136 oext = argv[arg] + 2;
137 if (!*oext)
138 oext = argv[++arg]; // space left after -i: use next argument
139
140 if (strncasecmp(oext, "MIME", 4) == 0) {
141 // get the MIME type from the next argument
142 oext = argv[++arg];
143 pOutFormat = Conv.FormatFromMIME(oext);
144 } else
145 pOutFormat = Conv.FindFormat(oext);
146
147 if (pOutFormat == nullptr) {
148 cerr << program_name << ": cannot write output format!" << endl;
149 usage();
150 exit(1);
151 }
152 break;
153
154 case 'O':
155 OutputFileName = argv[arg] + 2;
156 if (OutputFileName.empty())
157 OutputFileName =
158 argv[++arg]; // space left after -O: use next argument
159 break;
160
161 case 'L': // display a list of plugin type or classes
162 {
163 const char* param = nullptr;
164 if (argc > arg + 1) param = argv[arg + 2];
165
166 // First assume first arg is a plugin type and
167 // param is a subtype, like babel -L ops gen3D
168 // or first arg is a plugin ID, like babel -L cml
169 OBPlugin* plugin;
170 if ((OBPlugin::GetPlugin("plugins", argv[arg + 1]) &&
171 (plugin = OBPlugin::GetPlugin(argv[arg + 1], param))) ||
172 (plugin = OBPlugin::GetPlugin(nullptr, argv[arg + 1]))) {
173 // Output details of subtype
174 string txt;
175 plugin->Display(txt, "verbose", argv[arg + 1]);
176 cout << "One of the " << plugin->TypeID() << '\n' << txt << endl;
177 return 0;
178 }
179 //...otherwise assume it is a plugin type, like babel -L forcefields
180 // Output list of subtypes
181 OBPlugin::List(argv[arg + 1], param);
182 return 0;
183 }
184 case '?':
185 case 'H':
186 if (isalnum(argv[arg][2]) || arg == argc - 2) {
187 if (strncasecmp(argv[arg] + 2, "all", 3)) {
188 OBFormat* pFormat = (arg == argc - 2) ?
189 Conv.FindFormat(argv[arg + 1]) :
190 Conv.FindFormat(argv[arg] + 2);
191 if (pFormat) {
192 cout << argv[arg] + 2 << " " << pFormat->Description() << endl;
193 if (pFormat->Flags() & NOTWRITABLE)
194 cout << " This format is Read-only" << endl;
195 if (pFormat->Flags() & NOTREADABLE)
196 cout << " This format is Write-only" << endl;
197
198 if (strlen(pFormat->SpecificationURL()))
199 cout << "Specification at: " << pFormat->SpecificationURL()
200 << endl;
201 } else
202 cout << "Format type: " << argv[arg] + 2
203 << " was not recognized" << endl;
204 } else {
205 OBPlugin::List("formats", "verbose");
206 }
207 } else
208 help();
209 return 0;
210
211 case '-': // long option --name text
212 {
213 // Option's text is in the next and subsequent args, until one
214 // starts with -
215 char* nam = argv[arg] + 2;
216 if (!strcasecmp(nam, "help")) // special case handled here
217 {
218 help();
219 return 0;
220 }
221 if (*nam != '\0') // Do nothing if name is empty
222 {
223 string txt;
224 while (arg < argc - 1 && *argv[arg + 1] != '-') {
225 // use text from subsequent args
226 if (!txt.empty())
227 txt += ' '; //..space separated if more than one
228 txt += argv[++arg];
229 }
230
231 // If a API directive, e.g.---errorlevel
232 // send to the pseudoformat "obapi" (without any leading -)
233 if (*nam == '-') {
234 OBConversion apiConv;
235 OBFormat* pAPI = OBConversion::FindFormat("obapi");
236 if (pAPI) {
237 apiConv.SetOutFormat(pAPI);
238 apiConv.AddOption(nam + 1, OBConversion::GENOPTIONS,
239 txt.c_str());
240 apiConv.Write(nullptr, &std::cout);
241 }
242 } else
243 // Is a normal long option name, e.g --addtotitle
244 Conv.AddOption(nam, OBConversion::GENOPTIONS, txt.c_str());
245 }
246 } break;
247
248 case 'm': // multiple output files
249 SplitOrBatch = true;
250 break;
251
252 case 'a': // single character input option
253 p = argv[arg] + 2;
254 DoOption(p, Conv, OBConversion::INOPTIONS, arg, argc, argv);
255 break;
256
257 case 'x': // single character output option
258 p = argv[arg] + 2;
259 DoOption(p, Conv, OBConversion::OUTOPTIONS, arg, argc, argv);
260 break;
261
262 // Not essential, but allows these options to be before input
263 // filenames since we know they take one parameter, and are the most
264 // likely options to be misplaced
265 case 'f':
266 case 'l':
267 p = argv[arg] + 2;
268 if (!*p) p = argv[++arg]; // space left after -f: use next argument
269 Conv.AddOption(opchar, OBConversion::GENOPTIONS, p);
270 break;
271
272 case ':':
273 // e.g. -:c1ccccc1. SMILES passed as a file name and handled in
274 // OBConversion
275 FileList.push_back(argv[arg]);
276 break;
277
278 default: // single character general option
279 p = argv[arg] + 1;
280 DoOption(p, Conv, OBConversion::GENOPTIONS, arg, argc, argv);
281 break;
282 }
283 } else // filenames
284 FileList.push_back(argv[arg]);
285 }
286 }
287
288 // user didn't specify input and output format in commandline option
289 // try to parse it from program name (pdb2omd means input format is pdb,
290 // output format is omd)
291
292 string formatName(program_name);
293 pos = formatName.find_first_of("2");
294 if (pos != string::npos) {
295 if (!gotInType) {
296 string tmpExt = formatName.substr(0, pos);
297 pInFormat = Conv.FindFormat(tmpExt.c_str());
298 if (pInFormat == NULL) {
299 cerr << program_name << ": cannot read input format!" << endl;
300 usage();
301 } else {
302 gotInType = true;
303 inputExt = tmpExt;
304 }
305 }
306
307 if (!gotOutType) {
308 string tmpExt = formatName.substr(pos + 1, string::npos);
309 pOutFormat = Conv.FindFormat(tmpExt.c_str());
310 if (pOutFormat == NULL) {
311 cerr << program_name << ": cannot write output format!" << endl;
312 usage();
313 } else {
314 gotOutType = true;
315 outputExt = tmpExt;
316 }
317 }
318 }
319
320 if (!gotOutType) // the last file is the output
321 {
322 if (FileList.empty()) {
323 cerr << "No output file or format spec!" << endl;
324 usage();
325 }
326 OutputFileName = FileList.back();
327 FileList.pop_back();
328 }
329
330#if defined(_WIN32) && defined(USING_DYNAMIC_LIBS)
331 // Expand wildcards in input filenames and add to FileList
332 vector<string> tempFileList(FileList);
333 FileList.clear();
334 vector<string>::iterator itr;
335 for (itr = tempFileList.begin(); itr != tempFileList.end(); ++itr) {
336 if ((*itr)[0] == '-')
337 FileList.push_back(*itr);
338 else
339 DLHandler::findFiles(FileList, *itr);
340 }
341#endif
342
343 if (!gotInType) {
344 if (FileList.empty()) {
345 cerr << "No input file or format spec or possibly a misplaced option.\n"
346 "Most options must come after the input files. (-i -o -O -m can "
347 "be "
348 "anywhwere.)\n"
349 << endl;
350 usage();
351 exit(1);
352 }
353 }
354
355 if (!gotOutType) {
356 // check there is a valid output format, but the extension will be
357 // re-interpreted in OBConversion
358 pOutFormat = Conv.FormatFromExt(OutputFileName.c_str(), outGzip);
359 if (OutputFileName.empty() || pOutFormat == nullptr) {
360 cerr << "Missing or unknown output file or format spec or possibly a "
361 "misplaced "
362 "option.\n"
363 "Options, other than -i -o -O -m, must come after the input "
364 "files.\n"
365 << endl;
366 usage();
367 exit(1);
368 }
369 }
370
371 if (!Conv.SetInFormat(pInFormat)) // rely on autodetection for gzipped input
372 {
373 cerr << "Invalid input format" << endl;
374 usage();
375 exit(1);
376 }
377 if (!Conv.SetOutFormat(pOutFormat, outGzip)) {
378 cerr << "Invalid output format" << endl;
379 usage();
380 exit(1);
381 }
382
383 if (SplitOrBatch) {
384 // Put * into output file name before extension (or ext.gz)
385 if (OutputFileName.empty()) {
386 OutputFileName = "*.";
387 OutputFileName += oext;
388 } else {
389 string::size_type pos = OutputFileName.rfind(".gz");
390 if (pos == string::npos)
391 pos = OutputFileName.rfind('.');
392 else
393 pos = OutputFileName.rfind('.', pos - 1);
394 if (pos == string::npos)
395 OutputFileName += '*';
396 else
397 OutputFileName.insert(pos, "*");
398 }
399 }
400
401 int count = Conv.FullConvert(FileList, OutputFileName, OutputFileList);
402
403 Conv.ReportNumberConverted(count);
404
405 if (OutputFileList.size() > 1) {
406 clog << OutputFileList.size() << " files output. The first is "
407 << OutputFileList[0] << endl;
408 }
409
410 std::string messageSummary = obErrorLog.GetMessageSummary();
411 if (messageSummary.size()) { clog << messageSummary << endl; }
412
413#ifdef DEBUG
414 // CM keep window open
415 cout << "Press any key to finish" << endl;
416 getch();
417#endif
418
419 return 0;
420}
421
422void DoOption(const char* p, OBConversion& Conv, OBConversion::Option_type typ,
423 int& arg, int argc, char* argv[]) {
424 // Unlike babel, cannot have multiple concatenated single char options
425 // accepts: -sCCC -s CCC -s"CCC" -s CCC red -sCCC red
426 char ch[2] = "?";
427 *ch = *p++;
428 std::string txt;
429 // Get the option text
430 if (*p)
431 txt = p; // use text immediately following the option letter, and keep
432 // looking
433
434 while (arg < argc - 1 && *argv[arg + 1] != '-') {
435 // use text from subsequent args
436 if (!txt.empty()) txt += ' '; //..space separated if more than one
437 txt += argv[++arg];
438 }
439 Conv.AddOption(ch, typ, txt.c_str());
440}
441
442void usage() {
443 cout << program_name << ": part of OpenMD " << OPENMD_VERSION_MAJOR << "."
444 << OPENMD_VERSION_MINOR << "." << OPENMD_VERSION_TINY
445 << " and OpenBabel " << BABEL_VERSION << " -- " << __DATE__ << " -- "
446 << __TIME__ << endl;
447 cout << "Usage: " << program_name
448 << " [-i<input-type>] <name> [-o<output-type>] <name>" << endl;
449 cout << "Try -H option for more information." << endl;
450
451#ifdef DEBUG
452 // CM keep window open
453 cout << "Press any key to finish" << endl;
454 getch();
455#endif
456 exit(0);
457}
458
459void help() {
460 cout << "Open Babel converts chemical structures from one file format to "
461 "another"
462 << endl
463 << endl;
464 cout << "Usage: " << endl;
465 cout << program_name
466 << "[-i<input-type>] <infilename> [-o<output-type>] -O<outfilename> "
467 "[Options]"
468 << endl;
469 cout << "The extension of a file decides the format, unless it is overridden"
470 << endl;
471 cout << " by -i or -o options, e.g. -icml, or -o smi" << endl;
472 cout << "See below for available format-types, which are the same as the "
473 << endl;
474 cout << "file extensions and are case independent." << endl;
475 cout
476 << "If no input or output file is given stdin or stdout are used instead."
477 << endl
478 << endl;
479 cout
480 << "More than one input file can be specified and their names can contain"
481 << endl;
482 cout << "wildcard chars (* and ?). The format of each file can be different "
483 "unless"
484 << endl;
485 cout << "the -i option has been used, when they are all the same." << endl;
486 cout << "By default, the molecules are aggregated in the output file,"
487 << endl;
488 cout << " but see -m option, Splitting, below.\n" << endl;
489
490 cout << "Options, other than -i -o -O -m, must come after the input files.\n"
491 << endl;
492 cout << OBConversion::Description(); // Conversion options
493 cout << "-H Outputs this help text" << endl;
494 cout << "-Hxxx (xxx is file format ID e.g. -Hcml) gives format info" << endl;
495 cout << "-Hall Outputs details of all formats" << endl;
496 cout << "-V Outputs version number" << endl;
497 cout << "-L <category> Lists plugin classes of this category, e.g. <formats>"
498 << endl;
499 cout << " Use just -L for a list of plugin categories." << endl;
500 cout << " Use -L <ID> e.g. -L sdf for details of a format or other plugin."
501 << endl;
502 cout << "-m Produces multiple output files, to allow:" << endl;
503 cout << " Splitting: e.g. " << program_name
504 << " infile.mol -O new.smi -m" << endl;
505 cout << " puts each molecule into new1.smi new2.smi etc" << endl;
506 cout << " Batch conversion: e.g. " << program_name << " *.mol -osmi -m"
507 << endl;
508 cout << " converts each input file to a .smi file" << endl;
509#ifdef _WIN32
510 cout << " In Windows these can also be done using the forms" << endl;
511 cout << " " << program_name << " infile.mol -O new*.smi and "
512 << program_name << " *.mol -O *.smi respectively.\n"
513 << endl;
514#endif
515
516 OBFormat* pDefault = OBConversion::GetDefaultFormat();
517 if (pDefault)
518 cout << pDefault->TargetClassDescription(); // some more options probably
519 // for OBMol
520
521 OBFormat* pAPI = OBConversion::FindFormat("obapi");
522 if (pAPI) cout << pAPI->Description();
523
524 cout << "To see a list of recognized file formats use\n babel -L formats "
525 "[read] "
526 "[write]\n"
527 << "To see details and specific options for a particular format, e.g "
528 "CML, use\n "
529 "babel -L cml\n"
530 << endl;
531 // cout << "The following file formats are recognized:" << endl;
532 // OBPlugin::List("formats");
533 // cout << "\nSee further specific info and options using -H<format-type>,
534 // e.g. -Hcml"
535 // << endl;
536}