Ejemplo n.º 1
0
void
SelectionCollection::initOptions(Options             *options,
                                 SelectionTypeOption  selectionTypeOption)
{
    const char * const debug_levels[]
        = { "no", "basic", "compile", "eval", "full" };

    const char *const *postypes = PositionCalculationCollection::typeEnumValues;
    options->addOption(StringOption("selrpos")
                           .enumValueFromNullTerminatedArray(postypes)
                           .store(&impl_->rpost_).defaultValue(postypes[0])
                           .description("Selection reference positions"));
    if (selectionTypeOption == IncludeSelectionTypeOption)
    {
        options->addOption(StringOption("seltype")
                               .enumValueFromNullTerminatedArray(postypes)
                               .store(&impl_->spost_).defaultValue(postypes[0])
                               .description("Default selection output positions"));
    }
    else
    {
        impl_->spost_ = postypes[0];
    }
    GMX_RELEASE_ASSERT(impl_->debugLevel_ >= 0 && impl_->debugLevel_ <= 4,
                       "Debug level out of range");
    options->addOption(StringOption("seldebug").hidden(impl_->debugLevel_ == 0)
                           .enumValue(debug_levels)
                           .defaultValue(debug_levels[impl_->debugLevel_])
                           .storeEnumIndex(&impl_->debugLevel_)
                           .description("Print out selection trees for debugging"));
}
Ejemplo n.º 2
0
void FileNameOptionManager::addDefaultFileNameOption(
        IOptionsContainer *options, const char *name)
{
    options->addOption(
            StringOption(name).store(&impl_->defaultFileName_)
                .description("Set the default filename for all file options"));
}
void TimeUnitManager::addTimeUnitOption(Options *options, const char *name)
{
    options->addOption(StringOption(name).enumValue(g_timeUnits)
                           .defaultValue(g_timeUnits[timeUnit()])
                           .storeEnumIndex(&timeUnit_)
                           .description("Unit for time values"));
}
Ejemplo n.º 4
0
void
AnalysisDataPlotSettings::initOptions(IOptionsContainer *options)
{
    options->addOption(StringOption("xvg").enumValue(g_plotFormats)
                           .defaultValue("xmgrace")
                           .storeEnumIndex(&plotFormat_)
                           .description("Plot formatting"));
}
Ejemplo n.º 5
0
void CmdLineOptions::add_optional(std::string const &name,
                                  std::string const &description,
                                  std::string const &default_value) {
  std::unique_lock<std::mutex> l(mutex_);
  if (is_arg(name) || is_switch(name)) {
    throw OptionDefined();
  }
  std::string niceName(name);
  trim(niceName);
  argOptionList_.push_back(StringOption(niceName, description, default_value));
}
Ejemplo n.º 6
0
Options *
SelectionCollection::initOptions()
{
    static const char * const debug_levels[]
        = {"no", "basic", "compile", "eval", "full", NULL};
    /*
    static const char * const desc[] = {
        "This program supports selections in addition to traditional",
        "index files. Use [TT]-select help[tt] for additional information,",
        "or type 'help' in the selection prompt.",
        NULL,
    };
    options.setDescription(desc);
    */

    Options &options = _impl->_options;
    const char **postypes = gmx_ana_poscalc_create_type_enum(true);
    if (postypes == NULL)
    {
        // TODO: Use an out-of-memory exception here
        GMX_THROW(InternalError("Could not create position calculation enum"));
    }
    options.addOption(StringOption("selrpos").enumValue(postypes + 1)
                      .store(&_impl->_rpost).defaultValue(postypes[1])
                      .description("Selection reference positions"));
    options.addOption(StringOption("seltype").enumValue(postypes + 1)
                      .store(&_impl->_spost).defaultValue(postypes[1])
                      .description("Default selection output positions"));
    GMX_RELEASE_ASSERT(_impl->_debugLevel >= 0 && _impl->_debugLevel <= 4,
                       "Debug level out of range");
    options.addOption(StringOption("seldebug").hidden(_impl->_debugLevel == 0)
                      .enumValue(debug_levels)
                      .defaultValue(debug_levels[_impl->_debugLevel])
                      .storeEnumIndex(&_impl->_debugLevel)
                      .description("Print out selection trees for debugging"));
    sfree(postypes);

    return &_impl->_options;
}
Ejemplo n.º 7
0
int CommandLineHelpModule::run(int argc, char *argv[])
{
    // Add internal topics lazily here.
    addTopic(HelpTopicPointer(new CommandsHelpTopic(*impl_)), false);

    const char *const exportFormats[] = { "rst", "completion" };
    std::string       exportFormat;
    Options           options(NULL, NULL);
    options.addOption(StringOption("export").store(&exportFormat)
                          .enumValue(exportFormats));
    CommandLineParser(&options).parse(&argc, argv);
    if (!exportFormat.empty())
    {
        ModificationCheckingFileOutputRedirector redirector(impl_->outputRedirector_);
        const std::unique_ptr<IHelpExport>       exporter(
                impl_->createExporter(exportFormat, &redirector));
        impl_->exportHelp(exporter.get());
        return 0;
    }

    TextOutputStream      &outputFile = impl_->outputRedirector_->standardOutput();
    TextWriter             writer(&outputFile);
    HelpLinks              links(eHelpOutputFormat_Console);
    initProgramLinks(&links, *impl_);
    CommandLineHelpContext context(&writer, eHelpOutputFormat_Console, &links,
                                   impl_->binaryName_);
    context.setShowHidden(impl_->bHidden_);
    if (impl_->moduleOverride_ != NULL)
    {
        context.setModuleDisplayName(impl_->programContext_.displayName());
        impl_->moduleOverride_->writeHelp(context);
        return 0;
    }
    impl_->context_ = &context;

    HelpManager helpManager(impl_->rootTopic_, context.writerContext());
    try
    {
        for (int i = 1; i < argc; ++i)
        {
            helpManager.enterTopic(argv[i]);
        }
    }
    catch (const InvalidInputError &ex)
    {
        fprintf(stderr, "%s\n", ex.what());
        return 2;
    }
    helpManager.writeCurrentTopic();
    return 0;
}
Ejemplo n.º 8
0
//! \cond internal
void initTestUtils(const char *dataPath, const char *tempPath, bool usesMpi,
                   bool usesHardwareDetection, int *argc, char ***argv)
{
#if !defined NDEBUG &&                                                  \
    !((defined __clang__ || (defined(__GNUC__) && !defined(__ICC) && __GNUC__ == 7)) \
    && defined __OPTIMIZE__)
    gmx_feenableexcept();
#endif
    const CommandLineProgramContext &context = initForCommandLine(argc, argv);
    try
    {
        if (!usesMpi && gmx_node_num() > 1)
        {
            // We cannot continue, since some tests might be using
            // MPI_COMM_WORLD, which could deadlock if we would only
            // continue with the master rank here.
            if (gmx_node_rank() == 0)
            {
                fprintf(stderr, "NOTE: You are running %s on %d MPI ranks, "
                        "but it is does not contain MPI-enabled tests. "
                        "The test will now exit.\n",
                        context.programName(), gmx_node_num());
            }
            finalizeForCommandLine();
            std::exit(1);
        }
        if (usesHardwareDetection)
        {
            callAddGlobalTestEnvironment();
        }
        g_testContext = gmx::compat::make_unique<TestProgramContext>(context);
        setProgramContext(g_testContext.get());
        // Use the default finder that does not respect GMXLIB, since the tests
        // generally can only get confused by a different set of data files.
        setLibraryFileFinder(nullptr);
        ::testing::InitGoogleMock(argc, *argv);
        if (dataPath != nullptr)
        {
            TestFileManager::setInputDataDirectory(
                    Path::join(CMAKE_SOURCE_DIR, dataPath));
        }
        if (tempPath != nullptr)
        {
            TestFileManager::setGlobalOutputTempDirectory(tempPath);
        }
        bool        bHelp = false;
        std::string sourceRoot;
        Options     options;
        // TODO: A single option that accepts multiple names would be nicer.
        // Also, we recognize -help, but GTest doesn't, which leads to a bit
        // unintuitive behavior.
        options.addOption(BooleanOption("h").store(&bHelp)
                              .description("Print GROMACS-specific unit test options"));
        options.addOption(BooleanOption("help").store(&bHelp).hidden());
        options.addOption(BooleanOption("?").store(&bHelp).hidden());
        // TODO: Make this into a FileNameOption (or a DirectoryNameOption).
        options.addOption(StringOption("src-root").store(&sourceRoot)
                              .description("Override source tree location (for data files)"));
        // The potential MPI test event listener must be initialized first,
        // because it should appear in the start of the event listener list,
        // before other event listeners that may generate test failures
        // (currently, such an event listener is used by the reference data
        // framework).
        if (usesMpi)
        {
            initMPIOutput();
        }
        // TODO: Consider removing this option from test binaries that do not need it.
        initReferenceData(&options);
        initTestOptions(&options);
        try
        {
            CommandLineParser(&options).parse(argc, *argv);
            options.finish();
        }
        catch (const UserInputError &)
        {
            printHelp(options);
            throw;
        }
        if (bHelp)
        {
            printHelp(options);
        }
        if (!sourceRoot.empty())
        {
            g_testContext->overrideSourceRoot(sourceRoot);
            TestFileManager::setInputDataDirectory(
                    Path::join(sourceRoot, dataPath));
        }
    }
    catch (const std::exception &ex)
    {
        printFatalErrorMessage(stderr, ex);
        int retcode = processExceptionAtExitForCommandLine(ex);
        // TODO: It could be nice to destroy things in proper order such that
        // g_testContext would not contain hanging references at this point,
        // but in practice that should not matter.
        g_testContext.reset();
        std::exit(retcode);
    }
}
Ejemplo n.º 9
0
//! \cond
GMX_TEST_OPTIONS(IntegrationTestOptions, options)
{
    options->addOption(StringOption("max-backup")
                           .store(&IntegrationTestFixture::s_maxBackup)
                           .description("Maximum number of backup files of old test output to write (-1 prevents backups being created)"));
}
Ejemplo n.º 10
0
//! \cond internal
void initTestUtils(const char *dataPath, const char *tempPath, int *argc, char ***argv)
{
#ifndef NDEBUG
    gmx_feenableexcept();
#endif
    const CommandLineProgramContext &context = initForCommandLine(argc, argv);
    try
    {
        g_testContext.reset(new TestProgramContext(context));
        setProgramContext(g_testContext.get());
        // Use the default finder that does not respect GMXLIB, since the tests
        // generally can only get confused by a different set of data files.
        setLibraryFileFinder(NULL);
        ::testing::InitGoogleMock(argc, *argv);
        if (dataPath != NULL)
        {
            TestFileManager::setInputDataDirectory(
                    Path::join(CMAKE_SOURCE_DIR, dataPath));
        }
        if (tempPath != NULL)
        {
            TestFileManager::setGlobalOutputTempDirectory(tempPath);
        }
        bool        bHelp = false;
        std::string sourceRoot;
        Options     options(NULL, NULL);
        // TODO: A single option that accepts multiple names would be nicer.
        // Also, we recognize -help, but GTest doesn't, which leads to a bit
        // unintuitive behavior.
        options.addOption(BooleanOption("h").store(&bHelp)
                              .description("Print GROMACS-specific unit test options"));
        options.addOption(BooleanOption("help").store(&bHelp).hidden());
        options.addOption(BooleanOption("?").store(&bHelp).hidden());
        // TODO: Make this into a FileNameOption (or a DirectoryNameOption).
        options.addOption(StringOption("src-root").store(&sourceRoot)
                              .description("Override source tree location (for data files)"));
        // The potential MPI test event listener must be initialized first,
        // because it should appear in the start of the event listener list,
        // before other event listeners that may generate test failures
        // (currently, such an event listener is used by the reference data
        // framework).
        initMPIOutput();
        // TODO: Consider removing this option from test binaries that do not need it.
        initReferenceData(&options);
        initTestOptions(&options);
        try
        {
            CommandLineParser(&options).parse(argc, *argv);
            options.finish();
        }
        catch (const UserInputError &)
        {
            printHelp(options);
            throw;
        }
        if (bHelp)
        {
            printHelp(options);
        }
        if (!sourceRoot.empty())
        {
            g_testContext->overrideSourceRoot(sourceRoot);
            TestFileManager::setInputDataDirectory(
                    Path::join(sourceRoot, dataPath));
        }
    }
    catch (const std::exception &ex)
    {
        printFatalErrorMessage(stderr, ex);
        int retcode = processExceptionAtExitForCommandLine(ex);
        // TODO: It could be nice to destroy things in proper order such that
        // g_testContext would not contain hanging references at this point,
        // but in practice that should not matter.
        g_testContext.reset();
        std::exit(retcode);
    }
}
Ejemplo n.º 11
0
void
Angle::initOptions(Options *options, TrajectoryAnalysisSettings * /*settings*/)
{
    static const char *const desc[] = {
        "g_angle computes different types of angles between vectors.",
        "It supports both vectors defined by two positions and normals of",
        "planes defined by three positions.",
        "The z axis or the local normal of a sphere can also be used as",
        "one of the vectors.",
        "There are also convenience options 'angle' and 'dihedral' for",
        "calculating bond angles and dihedrals defined by three/four",
        "positions.[PAR]",
        "The type of the angle is specified with [TT]-g1[tt] and [TT]-g2[tt].",
        "If [TT]-g1[tt] is [TT]angle[tt] or [TT]dihedral[tt], [TT]-g2[tt]",
        "should not be specified.",
        "In this case, [TT]-group1[tt] should specify one selection,",
        "and it should contain triplets or quartets of positions that define",
        "the angles to be calculated.[PAR]",
        "If [TT]-g1[tt] is [TT]vector[tt] or [TT]plane[tt], [TT]-group1[tt]",
        "should specify a selection that has either pairs ([TT]vector[tt])",
        "or triplets ([TT]plane[tt]) of positions. For vectors, the positions",
        "set the endpoints of the vector, and for planes, the three positions",
        "are used to calculate the normal of the plane. In both cases,",
        "[TT]-g2[tt] specifies the other vector to use (see below).[PAR]",
        "With [TT]-g2 vector[tt] or [TT]-g2 plane[tt], [TT]-group2[tt] should",
        "specify another set of vectors. Both selections should specify the",
        "same number of vectors.[PAR]",
        "With [TT]-g2 sphnorm[tt], [TT]-group2[tt] should specify a single",
        "position that is the center of the sphere. The second vector is then",
        "calculated as the vector from the center to the midpoint of the",
        "positions specified by [TT]-group1[tt].[PAR]",
        "With [TT]-g2 z[tt], [TT]-group2[tt] is not necessary, and angles",
        "between the first vectors and the positive Z axis are calculated.[PAR]",
        "With [TT]-g2 t0[tt], [TT]-group2[tt] is not necessary, and angles",
        "are calculated from the vectors as they are in the first frame.[PAR]",
        "There are two options for output:",
        "[TT]-oav[tt] writes an xvgr file with the time and the average angle",
        "for each frame.",
        "[TT]-oall[tt] writes all the individual angles."
        /* TODO: Consider if the dump option is necessary and how to best
         * implement it.
           "[TT]-od[tt] can be used to dump all the individual angles,",
           "each on a separate line. This format is better suited for",
           "further processing, e.g., if angles from multiple runs are needed."
         */
    };
    static const char *const cGroup1TypeEnum[] =
    { "angle", "dihedral", "vector", "plane" };
    static const char *const cGroup2TypeEnum[] =
    { "none", "vector", "plane", "t0", "z", "sphnorm" };

    options->setDescription(concatenateStrings(desc));

    options->addOption(FileNameOption("oav").filetype(eftPlot).outputFile()
                           .store(&fnAverage_).defaultBasename("angaver")
                           .description("Average angles as a function of time"));
    options->addOption(FileNameOption("oall").filetype(eftPlot).outputFile()
                           .store(&fnAll_).defaultBasename("angles")
                           .description("All angles as a function of time"));
    // TODO: Add histogram output.

    options->addOption(StringOption("g1").enumValue(cGroup1TypeEnum)
                           .defaultEnumIndex(0).store(&g1type_)
                           .description("Type of analysis/first vector group"));
    options->addOption(StringOption("g2").enumValue(cGroup2TypeEnum)
                           .defaultEnumIndex(0).store(&g2type_)
                           .description("Type of second vector group"));

    // TODO: Allow multiple angles to be computed in one invocation.
    // Most of the code already supports it, but requires a solution for
    // Redmine issue #1010.
    // TODO: Consider what is the best way to support dynamic selections.
    // Again, most of the code already supports it, but it needs to be
    // considered how should -oall work, and additional checks should be added.
    sel1info_ = options->addOption(SelectionOption("group1")
                                       .required().onlyStatic().storeVector(&sel1_)
                                       .description("First analysis/vector selection"));
    sel2info_ = options->addOption(SelectionOption("group2")
                                       .onlyStatic().storeVector(&sel2_)
                                       .description("Second analysis/vector selection"));
}
Ejemplo n.º 12
0
double numberOrStringOptionChooser(const std::string &category, int index,
                                   const std::string &name, bool isNumber,
                                   const std::string &title,
                                   bool isInteractive, double minimum,
                                   double maximum, double step)
{
  double valn = 0.;
  std::string vals = "";
  if(isNumber)
    NumberOption(GMSH_GET, category.c_str(), index, name.c_str(), valn);
  else
    StringOption(GMSH_GET, category.c_str(), index, name.c_str(), vals);

  int nn = (isInteractive ? 2 : 3);
  int width = nn * BB + (nn + 1) * WB, height = 2 * BH + 3 * WB;
  std::string t = title;
  if(t.empty()) t = (isNumber ? "Number Chooser" : "String Chooser");
  Fl_Window *win = new paletteWindow(width, height, false, t.c_str());
  win->set_modal();
  win->hotspot(win);
  inputValueFloat *number = 0;
  Fl_Input *string = 0;
  if(isNumber){
    number = new inputValueFloat(WB, WB, width - 2 * WB, BH);
    number->value(valn);
    if(isInteractive){
      static opt_data d;
      d.category = category;
      d.index = index;
      d.name = name;
      number->minimum(minimum);
      number->maximum(maximum);
      number->step(step, 1);
      number->callback(interactive_cb, (void*)&d);
      number->when(FL_WHEN_RELEASE);
    }
  }
  else{
    string = new Fl_Input(WB, WB, width - 2 * WB, BH);
    string->value(vals.c_str());
  }
  Fl_Button *ok = new Fl_Return_Button
    (width - nn * BB - nn * WB, 2 * WB + BH, BB, BH, "OK");
  Fl_Button *def = new Fl_Button
    (width - (nn - 1) * BB - (nn - 1) * WB, 2 * WB + BH, BB, BH, "Default");
  Fl_Button *cancel = 0;
  if(!isInteractive)
    cancel = new Fl_Button
      (width - BB - WB, 2 * WB + BH, BB, BH, "Cancel");
  win->end();
  win->show();
  if(number) number->take_focus();
  if(string) string->take_focus();
  bool done = false;
  while(win->shown()){
    if(done) break;
    Fl::wait();
    for (;;) {
      Fl_Widget* o = Fl::readqueue();
      if (!o) break;
      if (o == win || o == cancel) {
        done = true;
        break;
      }
      if(o == ok){
        if(isNumber){
          valn = number->value();
          NumberOption(GMSH_SET|GMSH_GUI, category.c_str(), index,
                       name.c_str(), valn);
        }
        else{
          vals = string->value();
          StringOption(GMSH_SET|GMSH_GUI, category.c_str(), index,
                       name.c_str(), vals);
        }
        done = true;
        break;
      }
      if(o == def){
        if(isNumber){
          NumberOption(GMSH_GET_DEFAULT, category.c_str(), index,
                       name.c_str(), valn);
          number->value(valn);
          if(isInteractive) number->do_callback();
        }
        else{
          StringOption(GMSH_GET_DEFAULT, category.c_str(), index,
                       name.c_str(), vals);
          string->value(vals.c_str());
        }
        break;
      }
    }
  }
  delete win;

  if(isNumber){
    NumberOption(GMSH_GET, category.c_str(), index, name.c_str(), valn);
    return valn;
  }
  else
    return 0.;
}
Ejemplo n.º 13
0
int CommandLineHelpModule::run(int argc, char *argv[])
{
    // Add internal topics lazily here.
    addTopic(HelpTopicPointer(new CommandsHelpTopic(*impl_)));

    const char *const exportFormats[] = { "rst", "completion" };
    std::string       exportFormat;
    Options           options(NULL, NULL);
    options.addOption(StringOption("export").store(&exportFormat)
                          .enumValue(exportFormats));
    CommandLineParser(&options).parse(&argc, argv);
    if (!exportFormat.empty())
    {
        boost::scoped_ptr<HelpExportInterface> exporter;
        if (exportFormat == "rst")
        {
            exporter.reset(new HelpExportReStructuredText(*impl_));
        }
        else if (exportFormat == "completion")
        {
            exporter.reset(new HelpExportCompletion(*impl_));
        }
        else
        {
            GMX_THROW(NotImplementedError("This help format is not implemented"));
        }
        impl_->exportHelp(exporter.get());
        return 0;
    }

    File &outputFile = impl_->outputRedirector_->standardOutput();
    HelpLinks                                 links(eHelpOutputFormat_Console);
    initProgramLinks(&links, *impl_);
    boost::scoped_ptr<CommandLineHelpContext> context(
            new CommandLineHelpContext(&outputFile,
                                       eHelpOutputFormat_Console, &links));
    context->setShowHidden(impl_->bHidden_);
    if (impl_->moduleOverride_ != NULL)
    {
        context->setModuleDisplayName(impl_->programContext_.displayName());
        impl_->moduleOverride_->writeHelp(*context);
        return 0;
    }
    impl_->context_ = context.get();

    HelpManager helpManager(*impl_->rootTopic_, context->writerContext());
    try
    {
        for (int i = 1; i < argc; ++i)
        {
            helpManager.enterTopic(argv[i]);
        }
    }
    catch (const InvalidInputError &ex)
    {
        fprintf(stderr, "%s\n", ex.what());
        return 2;
    }
    helpManager.writeCurrentTopic();
    return 0;
}
Ejemplo n.º 14
0
Options *
Angle::initOptions(TrajectoryAnalysisSettings *settings)
{
    static const char *const desc[] = {
        "g_angle computes different types of angles between vectors.",
        "It supports both vectors defined by two positions and normals of",
        "planes defined by three positions.",
        "The z axis or the local normal of a sphere can also be used as",
        "one of the vectors.",
        "There are also convenience options 'angle' and 'dihedral' for",
        "calculating bond angles and dihedrals defined by three/four",
        "positions.[PAR]",
        "The type of the angle is specified with [TT]-g1[tt] and [TT]-g2[tt].",
        "If [TT]-g1[tt] is [TT]angle[tt] or [TT]dihedral[tt], [TT]-g2[tt]",
        "should not be specified.",
        "In this case, one selection is required, and it should contain",
        "triplets or quartets of positions that define the angles to be",
        "calculated.",
        "If [TT]-g1[tt] is not [TT]angle[tt] or [TT]dihedral[tt], [TT]-g2[tt]",
        "should not be [TT]none[tt], and the two options define two vectors",
        "for the calculation. For vectors ([TT]vector[tt]), a selection with",
        "pairs of positions is required, and for planes ([TT]plane[tt]),",
        "triplets of positions are required.",
        "If both vectors are specified by positions, the number of vectors",
        "should be the same in both selections.",
        "[TT]-g2 sphnorm[tt] requires a reference selection that defines",
        "the center of the sphere.",
        "[TT]-g2 z[tt] does not require any selection.[PAR]",
        "With [TT]-split1[tt], the positions for [TT]-g1[tt] are specified",
        "using N separate selections with M positions each, instead of the",
        "default M*N positions in one selection.",
        "[TT]-split2[tt] does the same for [TT]-g2[tt].[PAR]",
        "There are two options for output:",
        "[TT]-o[tt] writes an xvgr file with the time and the average angle",
        "for each frame.",
        "With [TT]-all[tt], also the individual angles are written (only",
        "supported for static selections).",
        "[TT]-od[tt] can be used to dump all the individual angles,",
        "each on a separate line. This format is better suited for",
        "further processing, e.g., if angles from multiple runs are needed.",
        NULL
    };
    static const char *const cGroup1TypeEnum[] =
        { "angle", "dihedral", "vector", "plane", NULL };
    static const char *const cGroup2TypeEnum[] =
        { "none", "vector", "plane", "t0", "z", "sphnorm", NULL };

    _options.setDescription(desc);

    _options.addOption(FileNameOption("o").filetype(eftPlot).writeOnly()
                           .store(&_fnAngle).defaultValueIfSet("angle"));
    _options.addOption(FileNameOption("od").filetype(eftPlot).writeOnly()
                           .store(&_fnDump).defaultValueIfSet("angdump"));

    _options.addOption(StringOption("g1").enumValue(cGroup1TypeEnum)
        .defaultEnumIndex(0).store(&_g1type)
        .description("Type of analysis/first vector group"));
    _options.addOption(StringOption("g2").enumValue(cGroup2TypeEnum)
        .defaultEnumIndex(0).store(&_g2type)
        .description("Type of second vector group"));
    _options.addOption(BooleanOption("split1").store(&_bSplit1)
        .description("Each position of first group in separate selection"));
    _options.addOption(BooleanOption("split2").store(&_bSplit2)
        .description("Each position of second group in separate selection"));
    _options.addOption(BooleanOption("multi").store(&_bMulti)
        .description("Analyze multiple sets of angles/dihedrals"));
    _options.addOption(BooleanOption("all").store(&_bAll)
        .description("Print individual angles together with the average"));
    _options.addOption(BooleanOption("dumpd").store(&_bDumpDist)
        .description("Write also distances with -od"));

    _options.addOption(SelectionOption("group1").multiValue().required()
        .dynamicOnlyWhole().storeVector(&_sel1).getAdjuster(&_sel1Adj)
        .description("First analysis/vector selection"));
    _options.addOption(SelectionOption("group2").multiValue()
        .dynamicOnlyWhole().storeVector(&_sel2).getAdjuster(&_sel2Adj)
        .description("Second analysis/vector selection"));

    return &_options;
}