SYCL: Avoid using no_init read accessor in rocFFT
[alexxy/gromacs.git] / src / gromacs / commandline / cmdlinemodulemanager.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
5  * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
6  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7  * and including many others, as listed in the AUTHORS file in the
8  * top-level source directory and at http://www.gromacs.org.
9  *
10  * GROMACS is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * as published by the Free Software Foundation; either version 2.1
13  * of the License, or (at your option) any later version.
14  *
15  * GROMACS is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with GROMACS; if not, see
22  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
24  *
25  * If you want to redistribute modifications to GROMACS, please
26  * consider that scientific software is very special. Version
27  * control is crucial - bugs must be traceable. We will be happy to
28  * consider code for inclusion in the official distribution, but
29  * derived work must not be called official GROMACS. Details are found
30  * in the README & COPYING files - if they are missing, get the
31  * official version at http://www.gromacs.org.
32  *
33  * To help us fund GROMACS development, we humbly ask that you cite
34  * the research papers on the package. Check out http://www.gromacs.org.
35  */
36 /*! \internal \file
37  * \brief
38  * Implements gmx::CommandLineModuleManager.
39  *
40  * \author Teemu Murtola <teemu.murtola@gmail.com>
41  * \ingroup module_commandline
42  */
43 #include "gmxpre.h"
44
45 #include "cmdlinemodulemanager.h"
46
47 #include <cstdio>
48
49 #include <string>
50 #include <utility>
51
52 #include "gromacs/commandline/cmdlinehelpcontext.h"
53 #include "gromacs/commandline/cmdlineinit.h"
54 #include "gromacs/commandline/cmdlinemodule.h"
55 #include "gromacs/commandline/cmdlineparser.h"
56 #include "gromacs/commandline/cmdlineprogramcontext.h"
57 #include "gromacs/math/utilities.h"
58 #include "gromacs/options/basicoptions.h"
59 #include "gromacs/options/options.h"
60 #include "gromacs/utility/basenetwork.h"
61 #include "gromacs/utility/coolstuff.h"
62 #include "gromacs/utility/exceptions.h"
63 #include "gromacs/utility/fatalerror.h"
64 #include "gromacs/utility/futil.h"
65 #include "gromacs/utility/gmxassert.h"
66 #include "gromacs/utility/stringutil.h"
67 #include "gromacs/utility/sysinfo.h"
68
69 #include "cmdlinehelpmodule.h"
70 #include "cmdlinemodulemanager_impl.h"
71
72 namespace gmx
73 {
74
75 namespace
76 {
77
78 //! \addtogroup module_commandline
79 //! \{
80
81 /********************************************************************
82  * CMainCommandLineModule
83  */
84
85 /*! \brief
86  * Implements a ICommandLineModule, given a function with C/C++ main()
87  * signature.
88  */
89 class CMainCommandLineModule : public ICommandLineModule
90 {
91 public:
92     //! \copydoc gmx::CommandLineModuleManager::CMainFunction
93     typedef CommandLineModuleManager::CMainFunction CMainFunction;
94     //! \copydoc gmx::CommandLineModuleManager::InitSettingsFunction
95     typedef CommandLineModuleManager::InitSettingsFunction InitSettingsFunction;
96
97     /*! \brief
98      * Creates a wrapper module for the given main function.
99      *
100      * \param[in] name             Name for the module.
101      * \param[in] shortDescription One-line description for the module.
102      * \param[in] mainFunction     Main function to wrap.
103      * \param[in] settingsFunction Initializer for settings (can be null).
104      *
105      * Does not throw.  This is essential for correct implementation of
106      * CommandLineModuleManager::runAsMainCMain().
107      */
108     CMainCommandLineModule(const char*          name,
109                            const char*          shortDescription,
110                            CMainFunction        mainFunction,
111                            InitSettingsFunction settingsFunction) :
112         name_(name),
113         shortDescription_(shortDescription),
114         mainFunction_(mainFunction),
115         settingsFunction_(settingsFunction)
116     {
117     }
118
119     const char* name() const override { return name_; }
120     const char* shortDescription() const override { return shortDescription_; }
121
122     void init(CommandLineModuleSettings* settings) override
123     {
124         if (settingsFunction_ != nullptr)
125         {
126             settingsFunction_(settings);
127         }
128     }
129     int  run(int argc, char* argv[]) override { return mainFunction_(argc, argv); }
130     void writeHelp(const CommandLineHelpContext& context) const override
131     {
132         writeCommandLineHelpCMain(context, name_, mainFunction_);
133     }
134
135 private:
136     const char*          name_;
137     const char*          shortDescription_;
138     CMainFunction        mainFunction_;
139     InitSettingsFunction settingsFunction_;
140 };
141
142 //! \}
143
144 } // namespace
145
146 /********************************************************************
147  * CommandLineCommonOptionsHolder
148  */
149
150 CommandLineCommonOptionsHolder::CommandLineCommonOptionsHolder() :
151     bHelp_(false),
152     bHidden_(false),
153     bQuiet_(false),
154     bVersion_(false),
155     bCopyright_(true),
156     niceLevel_(19),
157     bNiceSet_(false),
158     bBackup_(true),
159     bFpexcept_(false),
160     debugLevel_(0)
161 {
162     binaryInfoSettings_.copyright(true);
163 }
164
165 CommandLineCommonOptionsHolder::~CommandLineCommonOptionsHolder() {}
166
167 void CommandLineCommonOptionsHolder::initOptions()
168 {
169     options_.addOption(BooleanOption("h").store(&bHelp_).description("Print help and quit"));
170     options_.addOption(BooleanOption("hidden").store(&bHidden_).hidden().description(
171             "Show hidden options in help"));
172     options_.addOption(BooleanOption("quiet").store(&bQuiet_).description(
173             "Do not print common startup info or quotes"));
174     options_.addOption(
175             BooleanOption("version").store(&bVersion_).description("Print extended version information and quit"));
176     options_.addOption(
177             BooleanOption("copyright").store(&bCopyright_).description("Print copyright information on startup"));
178     options_.addOption(IntegerOption("nice")
179                                .store(&niceLevel_)
180                                .storeIsSet(&bNiceSet_)
181                                .description("Set the nicelevel (default depends on command)"));
182     options_.addOption(BooleanOption("backup").store(&bBackup_).description(
183             "Write backups if output files exist"));
184     options_.addOption(
185             BooleanOption("fpexcept").store(&bFpexcept_).hidden().description("Enable floating-point exceptions"));
186     options_.addOption(IntegerOption("debug")
187                                .store(&debugLevel_)
188                                .hidden()
189                                .defaultValueIfSet(1)
190                                .description("Write file with debug information, "
191                                             "1: short (default), 2: also x and f"));
192 }
193
194 bool CommandLineCommonOptionsHolder::finishOptions()
195 {
196     options_.finish();
197     binaryInfoSettings_.extendedInfo(bVersion_);
198     // The latter condition suppresses the copyright with
199     // -quiet -version.
200     binaryInfoSettings_.copyright(bCopyright_ && !bQuiet_);
201     return !bVersion_;
202 }
203
204 void CommandLineCommonOptionsHolder::adjustFromSettings(const CommandLineModuleSettings& settings)
205 {
206     if (!bNiceSet_)
207     {
208         niceLevel_ = settings.defaultNiceLevel();
209     }
210 }
211
212 /********************************************************************
213  * CommandLineModuleManager::Impl
214  */
215
216 /*! \internal \brief
217  * Private implementation class for CommandLineModuleManager.
218  *
219  * \ingroup module_commandline
220  */
221 class CommandLineModuleManager::Impl
222 {
223 public:
224     /*! \brief
225      * Initializes the implementation class.
226      *
227      * \param[in] binaryName     Name of the running binary
228      *     (without Gromacs binary suffix or .exe on Windows).
229      * \param     programContext Program information for the running binary.
230      */
231     Impl(const char* binaryName, CommandLineProgramContext* programContext);
232
233     /*! \brief
234      * Helper method that adds a given module to the module manager.
235      *
236      * \throws    std::bad_alloc if out of memory.
237      */
238     void addModule(CommandLineModulePointer module);
239     /*! \brief
240      * Creates the help module if it does not yet exist.
241      *
242      * \throws    std::bad_alloc if out of memory.
243      *
244      * This method should be called before accessing \a helpModule_.
245      */
246     void ensureHelpModuleExists();
247
248     /*! \brief
249      * Finds a module that matches a name.
250      *
251      * \param[in] name  Module name to find.
252      * \returns   Iterator to the found module, or
253      *      \c modules_.end() if not found.
254      *
255      * Does not throw.
256      */
257     CommandLineModuleMap::const_iterator findModuleByName(const std::string& name) const;
258
259     /*! \brief
260      * Processes command-line options for the wrapper binary.
261      *
262      * \param[in,out] optionsHolder Common options.
263      * \param[in,out] argc          On input, argc passed to run().
264      *     On output, argc to be passed to the module.
265      * \param[in,out] argv          On input, argv passed to run().
266      *     On output, argv to be passed to the module.
267      * \throws    InvalidInputError if there are invalid options.
268      * \returns   The module that should be run.
269      *
270      * Handles command-line options that affect the wrapper binary
271      * (potentially changing the members of \c this in response to the
272      * options).  Also finds the module that should be run and the
273      * arguments that should be passed to it.
274      */
275     ICommandLineModule* processCommonOptions(CommandLineCommonOptionsHolder* optionsHolder,
276                                              int*                            argc,
277                                              char***                         argv);
278
279     //! Prints the footer at the end of execution.
280     static void printThanks(FILE* fp);
281
282     /*! \brief
283      * Maps module names to module objects.
284      *
285      * Owns the contained modules.
286      */
287     CommandLineModuleMap modules_;
288     /*! \brief
289      * List of groupings for modules for help output.
290      *
291      * Owns the contained module group data objects.
292      * CommandLineModuleGroup objects point to the data objects contained
293      * here.
294      */
295     CommandLineModuleGroupList moduleGroups_;
296     //! Information about the currently running program.
297     CommandLineProgramContext& programContext_;
298     //! Name of the binary.
299     std::string binaryName_;
300     /*! \brief
301      * Module that implements help for the binary.
302      *
303      * The pointed module is owned by the \a modules_ container.
304      */
305     CommandLineHelpModule* helpModule_;
306     //! If non-NULL, run this module in single-module mode.
307     ICommandLineModule* singleModule_;
308     //! Stores the value set with setQuiet().
309     bool bQuiet_;
310
311 private:
312     GMX_DISALLOW_COPY_AND_ASSIGN(Impl);
313 };
314
315 CommandLineModuleManager::Impl::Impl(const char* binaryName, CommandLineProgramContext* programContext) :
316     programContext_(*programContext),
317     binaryName_(binaryName != nullptr ? binaryName : ""),
318     helpModule_(nullptr),
319     singleModule_(nullptr),
320     bQuiet_(false)
321 {
322     GMX_RELEASE_ASSERT(binaryName_.find('-') == std::string::npos,
323                        "Help export does not currently work with binary names with dashes");
324 }
325
326 void CommandLineModuleManager::Impl::addModule(CommandLineModulePointer module)
327 {
328     GMX_ASSERT(modules_.find(module->name()) == modules_.end(),
329                "Attempted to register a duplicate module name");
330     ensureHelpModuleExists();
331     HelpTopicPointer helpTopic(helpModule_->createModuleHelpTopic(*module));
332     modules_.insert(std::make_pair(std::string(module->name()), std::move(module)));
333     helpModule_->addTopic(std::move(helpTopic), false);
334 }
335
336 void CommandLineModuleManager::Impl::ensureHelpModuleExists()
337 {
338     if (helpModule_ == nullptr)
339     {
340         helpModule_ = new CommandLineHelpModule(programContext_, binaryName_, modules_, moduleGroups_);
341         addModule(CommandLineModulePointer(helpModule_));
342     }
343 }
344
345 CommandLineModuleMap::const_iterator CommandLineModuleManager::Impl::findModuleByName(const std::string& name) const
346 {
347     // TODO: Accept unambiguous prefixes?
348     return modules_.find(name);
349 }
350
351 ICommandLineModule* CommandLineModuleManager::Impl::processCommonOptions(CommandLineCommonOptionsHolder* optionsHolder,
352                                                                          int*    argc,
353                                                                          char*** argv)
354 {
355     // Check if we are directly invoking a certain module.
356     ICommandLineModule* module = singleModule_;
357
358     // TODO: It would be nice to propagate at least the -quiet option to
359     // the modules so that they can also be quiet in response to this.
360
361     if (module == nullptr)
362     {
363         // If not in single-module mode, process options to the wrapper binary.
364         // TODO: Ideally, this could be done by CommandLineParser.
365
366         // Find the module name (if any) in the arg list
367         int indexOfModuleName = 1;
368         while (indexOfModuleName < *argc && (*argv)[indexOfModuleName][0] == '-')
369         {
370             ++indexOfModuleName;
371         }
372         if (indexOfModuleName > 1)
373         {
374             // Process options that are provided to the wrapper
375             // binary. These precede the module name, if one exists.
376             int argcForWrapper = indexOfModuleName;
377             CommandLineParser(optionsHolder->options()).parse(&argcForWrapper, *argv);
378         }
379         // If no action requested and there is a module specified, process it.
380         if (indexOfModuleName < *argc && !optionsHolder->shouldIgnoreActualModule())
381         {
382             const char*                          moduleName = (*argv)[indexOfModuleName];
383             CommandLineModuleMap::const_iterator moduleIter = findModuleByName(moduleName);
384             if (moduleIter == modules_.end())
385             {
386                 std::string message = formatString("'%s' is not a GROMACS command.", moduleName);
387                 GMX_THROW(InvalidInputError(message));
388             }
389             module = moduleIter->second.get();
390             *argc -= indexOfModuleName;
391             *argv += indexOfModuleName;
392             // After this point, argc and argv are the same independent of
393             // which path is taken: (*argv)[0] is the module name.
394         }
395     }
396     if (module != nullptr)
397     {
398         if (singleModule_ == nullptr)
399         {
400             programContext_.setDisplayName(binaryName_ + " " + module->name());
401         }
402         // Recognize the common options also after the module name.
403         // TODO: It could be nicer to only recognize -h/-hidden if module is not
404         // null.
405         CommandLineParser(optionsHolder->options()).allowPositionalArguments(true).skipUnknown(true).parse(argc, *argv);
406     }
407     if (!optionsHolder->finishOptions())
408     {
409         return nullptr;
410     }
411     // If no module specified and no other action, show the help.
412     // Also explicitly specifying -h for the wrapper binary goes here.
413     if (module == nullptr || optionsHolder->shouldShowHelp())
414     {
415         ensureHelpModuleExists();
416         if (module != nullptr)
417         {
418             helpModule_->setModuleOverride(*module);
419         }
420         *argc  = 1;
421         module = helpModule_;
422     }
423     if (module == helpModule_)
424     {
425         helpModule_->setShowHidden(optionsHolder->shouldShowHidden());
426     }
427     return module;
428 }
429
430 void CommandLineModuleManager::Impl::printThanks(FILE* fp)
431 {
432     fprintf(fp, "\n%s\n\n", getCoolQuote().c_str());
433 }
434
435 /********************************************************************
436  * CommandLineModuleManager
437  */
438
439 CommandLineModuleManager::CommandLineModuleManager(const char*                binaryName,
440                                                    CommandLineProgramContext* programContext) :
441     impl_(new Impl(binaryName, programContext))
442 {
443 }
444
445 CommandLineModuleManager::~CommandLineModuleManager() {}
446
447 void CommandLineModuleManager::setQuiet(bool bQuiet)
448 {
449     impl_->bQuiet_ = bQuiet;
450 }
451
452 void CommandLineModuleManager::setOutputRedirector(IFileOutputRedirector* output)
453 {
454     impl_->ensureHelpModuleExists();
455     impl_->helpModule_->setOutputRedirector(output);
456 }
457
458 void CommandLineModuleManager::setSingleModule(ICommandLineModule* module)
459 {
460     impl_->singleModule_ = module;
461 }
462
463 void CommandLineModuleManager::addModule(CommandLineModulePointer module)
464 {
465     impl_->addModule(std::move(module));
466 }
467
468 void CommandLineModuleManager::addModuleCMain(const char* name, const char* shortDescription, CMainFunction mainFunction)
469 {
470     CommandLineModulePointer module(
471             new CMainCommandLineModule(name, shortDescription, mainFunction, nullptr));
472     addModule(std::move(module));
473 }
474
475 void CommandLineModuleManager::addModuleCMainWithSettings(const char*          name,
476                                                           const char*          shortDescription,
477                                                           CMainFunction        mainFunction,
478                                                           InitSettingsFunction settingsFunction)
479 {
480     CommandLineModulePointer module(
481             new CMainCommandLineModule(name, shortDescription, mainFunction, settingsFunction));
482     addModule(std::move(module));
483 }
484
485 CommandLineModuleGroup CommandLineModuleManager::addModuleGroup(const char* title)
486 {
487     const char* const                 binaryName = impl_->binaryName_.c_str();
488     CommandLineModuleGroupDataPointer group(
489             new CommandLineModuleGroupData(impl_->modules_, binaryName, title));
490     impl_->moduleGroups_.push_back(std::move(group));
491     return CommandLineModuleGroup(impl_->moduleGroups_.back().get());
492 }
493
494 void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
495 {
496     impl_->ensureHelpModuleExists();
497     impl_->helpModule_->addTopic(std::move(topic), true);
498 }
499
500 int CommandLineModuleManager::run(int argc, char* argv[])
501 {
502     ICommandLineModule*            module;
503     const bool                     bMaster = (gmx_node_rank() == 0);
504     bool                           bQuiet  = impl_->bQuiet_ || !bMaster;
505     CommandLineCommonOptionsHolder optionsHolder;
506     try
507     {
508         optionsHolder.initOptions();
509         module = impl_->processCommonOptions(&optionsHolder, &argc, &argv);
510     }
511     catch (const std::exception&)
512     {
513         bQuiet |= optionsHolder.shouldBeQuiet();
514         if (!bQuiet)
515         {
516             printBinaryInformation(stderr, impl_->programContext_, optionsHolder.binaryInfoSettings());
517         }
518         throw;
519     }
520     bQuiet |= optionsHolder.shouldBeQuiet();
521     if (!bQuiet)
522     {
523         FILE* out = optionsHolder.startupInfoFile();
524         printBinaryInformation(out, impl_->programContext_, optionsHolder.binaryInfoSettings());
525         fprintf(out, "\n");
526     }
527     if (module == nullptr)
528     {
529         return 0;
530     }
531
532     CommandLineModuleSettings settings;
533     module->init(&settings);
534     optionsHolder.adjustFromSettings(settings);
535
536     gmx_set_max_backup_count(optionsHolder.shouldBackup() ? -1 : 0);
537
538     // Open the debug file.
539     if (optionsHolder.debugLevel() > 0)
540     {
541         std::string filename(impl_->programContext_.programName());
542         if (gmx_node_num() > 1)
543         {
544             filename.append(formatString("%d", gmx_node_rank()));
545         }
546         filename.append(".debug");
547
548         fprintf(stderr, "Will write debug log file: %s\n", filename.c_str());
549         gmx_init_debug(optionsHolder.debugLevel(), filename.c_str());
550     }
551     // Set the nice level unless disabled in the configuration.
552     if (optionsHolder.niceLevel() != 0)
553     {
554         static bool bNiceSet = false; // Only set it once.
555         if (!bNiceSet)
556         {
557             // TODO: Diagnostic if this fails and the user explicitly requested it.
558             gmx_set_nice(optionsHolder.niceLevel());
559             bNiceSet = true;
560         }
561     }
562     if (optionsHolder.enableFPExceptions())
563     {
564         // TODO: currently it is always enabled for mdrun (verlet) and tests.
565         gmx_feenableexcept();
566     }
567
568     int rc = 0;
569     if (!(module == impl_->helpModule_ && !bMaster))
570     {
571         rc = module->run(argc, argv);
572     }
573     if (!bQuiet)
574     {
575         impl_->printThanks(stderr);
576     }
577     return rc;
578 }
579
580 // static
581 int CommandLineModuleManager::runAsMainSingleModule(int argc, char* argv[], ICommandLineModule* module)
582 {
583     CommandLineProgramContext& programContext = gmx::initForCommandLine(&argc, &argv);
584     try
585     {
586         CommandLineModuleManager manager(nullptr, &programContext);
587         manager.setSingleModule(module);
588         int rc = manager.run(argc, argv);
589         gmx::finalizeForCommandLine();
590         return rc;
591     }
592     catch (const std::exception& ex)
593     {
594         printFatalErrorMessage(stderr, ex);
595         return processExceptionAtExitForCommandLine(ex);
596     }
597 }
598
599 // static
600 int CommandLineModuleManager::runAsMainCMain(int argc, char* argv[], CMainFunction mainFunction)
601 {
602     CMainCommandLineModule module(argv[0], nullptr, mainFunction, nullptr);
603     return runAsMainSingleModule(argc, argv, &module);
604 }
605
606 // static
607 int CommandLineModuleManager::runAsMainCMainWithSettings(int                  argc,
608                                                          char*                argv[],
609                                                          CMainFunction        mainFunction,
610                                                          InitSettingsFunction settingsFunction)
611 {
612     CMainCommandLineModule module(argv[0], nullptr, mainFunction, settingsFunction);
613     return runAsMainSingleModule(argc, argv, &module);
614 }
615
616 /********************************************************************
617  * CommandLineModuleGroupData
618  */
619
620 void CommandLineModuleGroupData::addModule(const char* name, const char* description)
621 {
622     CommandLineModuleMap::const_iterator moduleIter = allModules_.find(name);
623     GMX_RELEASE_ASSERT(moduleIter != allModules_.end(), "Non-existent module added to a group");
624     if (description == nullptr)
625     {
626         description = moduleIter->second->shortDescription();
627         GMX_RELEASE_ASSERT(description != nullptr, "Module without a description added to a group");
628     }
629     std::string tag(formatString("%s-%s", binaryName_, name));
630     modules_.push_back(std::make_pair(tag, description));
631 }
632
633 /********************************************************************
634  * CommandLineModuleGroup
635  */
636
637 void CommandLineModuleGroup::addModule(const char* name)
638 {
639     impl_->addModule(name, nullptr);
640 }
641
642 void CommandLineModuleGroup::addModuleWithDescription(const char* name, const char* description)
643 {
644     impl_->addModule(name, description);
645 }
646
647 } // namespace gmx