Command Line Interface and Options

This chapter describes how Options classes that are derived from OEOptions can be configured with command line inputs passed in through argv.

The OESystem.OEConfigureOpts is a convenience function that takes an OEOptions derivative options class and the command line arguments argv, parses the command line arguments, and configures the option parameters with command line inputs. A OEInterface is generated internally from the OEOptions, which performs the command lines parsing.

Note that this only applies to Options classes that are derived from the OEOptions. There are a handful of Options classes that still does not derive from OEOptions, and hence cannot be configured with command line inputs directly. Command Line Interface Definitions is an alternative way to work with command line interfaces for such cases.

In addition to the convenience function OESystem.OEConfigureOpts, two additional general purpose Options classes, OESimpleAppOptions, and OERefInputAppOptions, are provided that extends any other options classes to include necessary input/output parameters that are generally required to create an application.

Application with Input, output, and Options

The following example shows how to use the OESimpleAppOptions class to extend an Options class with an additional input and output file.

The specific example is taken from OEDocking, and uses the the OEMakeReceptor function to make a receptor in a design unit. In this example, the OEMakeReceptorOptions is extended with an input and output.

Listing 1: Application with input, output, and options (Making Receptor)

/*
(C) 2017 OpenEye Scientific Software Inc. All rights reserved.

TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
provided to current licensees or subscribers of OpenEye products or
SaaS offerings (each a "Customer").
Customer is hereby permitted to use, copy, and modify the Sample Code,
subject to these terms. OpenEye claims no rights to Customer's
modifications. Modification of Sample Code is at Customer's sole and
exclusive risk. Sample Code may require Customer to have a then
current license or subscription to the applicable OpenEye offering.
THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED.  OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall OpenEye be
liable for any damages or liability in connection with the Sample Code
or its use.
*/
#include "openeye.h"
#include "oesystem.h"
#include "oechem.h"
#include "oebio.h"
#include "oedocking.h"

using namespace OESystem;
using namespace OEChem;
using namespace OEBio;
using namespace OEDocking;

int main(int argc, char** argv)
{
  OEMakeReceptorOptions recOpts;
  OESimpleAppOptions opts(recOpts, "MakeReceptoor", OEChem::OEFileStringType::DU, OEChem::OEFileStringType::DU);
  if (OESystem::OEConfigureOpts(opts, argc, argv, false) == OESystem::OEOptsConfigureStatus::Help)
    return 0;

  OEPlatform::oeifstream ifs(opts.GetInFile());
  OEPlatform::oeofstream ofs(opts.GetOutFile());


  OEDesignUnit du;
  while (OEReadDesignUnit(ifs, du))
    if (OEMakeReceptor(du, recOpts))
      OEWriteDesignUnit(ofs, du);
    else
      OEThrow.Fatal(du.GetTitle() + " Failed to make a receptor");

  return 0;
}

Download code

MakeReceptor.cpp

Application with Reference system, Input, output, and Options

Besides an input and output, some application requires an additional reference system input. The OERefInputAppOptions class can be used to extend an options class with three additional parameters, an input, output, and a reference system.

The following code example is taken from OEDocking, and uses the OEDock object to perform docking. In this example, the OEDockOptions is extended with an input, output, and a receptor (i.e., the reference system).

Listing 2: Application with input, reference, output, and options (Docking Molecules)

/*
(C) 2017 OpenEye Scientific Software Inc. All rights reserved.

TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
provided to current licensees or subscribers of OpenEye products or
SaaS offerings (each a "Customer").
Customer is hereby permitted to use, copy, and modify the Sample Code,
subject to these terms. OpenEye claims no rights to Customer's
modifications. Modification of Sample Code is at Customer's sole and
exclusive risk. Sample Code may require Customer to have a then
current license or subscription to the applicable OpenEye offering.
THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED.  OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall OpenEye be
liable for any damages or liability in connection with the Sample Code
or its use.
*/
#include "openeye.h"
#include "oesystem.h"
#include "oechem.h"
#include "oebio.h"
#include "oedocking.h"

using namespace OESystem;
using namespace OEChem;
using namespace OEBio;
using namespace OEDocking;
using namespace std;

#include "DockMolecules.itf"

int main(int argc, char** argv)
{

  OEDockOptions dockOpts;
  OERefInputAppOptions opts(dockOpts, "DockMolecules", OEFileStringType::Mol3D,
      OEFileStringType::Mol3D, OEFileStringType::DU, "-receptor");

  if (OEConfigureOpts(opts, argc, argv, false) == OEOptsConfigureStatus::Help)
    return 1;

  dockOpts.UpdateValues(opts);

  oemolistream imstr(opts.GetInFile());
  oemolostream omstr(opts.GetOutFile());

  OEDesignUnit du;
  if (!OEReadDesignUnit(opts.GetRefFile(), du))
    OEThrow.Fatal("Unable to read du");

  if (!du.HasReceptor())
      OEThrow.Fatal("Design Unit " + du.GetTitle() + " does not contain a receptor");

  OEDock dock(dockOpts);
  dock.Initialize(du);

  OEMol mcmol;
  while (OEReadMolecule(imstr, mcmol))
  {
    OEGraphMol dockedMol;
    unsigned int retCode = dock.DockMultiConformerMolecule(dockedMol, mcmol);
    if (retCode != OEDockingReturnCode::Success)
      OEThrow.Fatal("Docking Failed with error code " + OEDockingReturnCodeGetName(retCode));
    string sdtag = OEDockMethodGetName(dockOpts.GetScoreMethod());
    OESetSDScore(dockedMol, dock, sdtag);
    dock.AnnotatePose(dockedMol);
    OEWriteMolecule(omstr, dockedMol);
  }
  return 0;
}

Download code

DockMolecules.cpp

Application with Input and output only

Some applications do not have any addtionl option or parameter, and only requires an input and output. The OESimpleAppOptions class has an overload that does not require an OEOptions input argument.

The following code example is taken from OEQuacpac, and uses the OEAssignCharges method to assign charges in a OEDesignUnit. This example only configures an input and output, and does not have any additional parameters to configure.

Listing 3: Application with input and output only (DesignUnit Charges)

/*
(C) 2021 OpenEye Scientific Software Inc. All rights reserved.

TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
provided to current licensees or subscribers of OpenEye products or
SaaS offerings (each a "Customer").
Customer is hereby permitted to use, copy, and modify the Sample Code,
subject to these terms. OpenEye claims no rights to Customer's
modifications. Modification of Sample Code is at Customer's sole and
exclusive risk. Sample Code may require Customer to have a then
current license or subscription to the applicable OpenEye offering.
THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED.  OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall OpenEye be
liable for any damages or liability in connection with the Sample Code
or its use.
*/

/*
######################################################################
# This program demonstrates how to apply charges to an OEDesignUnit.
######################################################################
*/

#include <openeye.h>

#include <oebio.h>
#include <oechem.h>
#include <oeplatform.h>
#include <oequacpac.h>
#include <oesystem.h>

using namespace std;
using namespace OEBio;
using namespace OEChem;
using namespace OEPlatform;
using namespace OEProton;
using namespace OESystem;


int main(int argc,char* argv[])
{
  OEChem::OESimpleAppOptions opts("applycharges_oedu", OEChem::OEFileStringType::DU, OEChem::OEFileStringType::DU);
  const bool writeSettings = false;
  if (OESystem::OEConfigureOpts(opts, argc, argv, writeSettings) == OESystem::OEOptsConfigureStatus::Help)
    return 0;

  std::string duFile(opts.GetInFile());
  std::string duOutFile(opts.GetOutFile());
  
  if (OEIsReadableDesignUnit(duFile))
  {
    OEPlatform::oeifstream ifs;
    if (!ifs.open(duFile))
      OESystem::OEThrow.Fatal("Unable to open %s for reading", opts.GetInFile().c_str());
  
    OEPlatform::oeofstream ofs;
    if (!ofs.open(duOutFile))
      OESystem::OEThrow.Fatal("Unable to open %s for writing", opts.GetOutFile().c_str());

    OEBio::OEDesignUnit du;
    while (OEReadDesignUnit(ifs, du))
    {
      OEProton::OEDesignUnitCharges ducharges;
      if (ducharges.ApplyCharges(du))
      {
        OEWriteDesignUnit(ofs, du);
      }
      else
      {
        OEThrow.Warning("%s: %s", (du.GetTitle(), "Failed to assign charges"));
      }
    }
  }
  else
  {
    OEThrow.Fatal("%s is not a valid OEDesignUnit file", duFile.c_str());
  }

  return 0;
}

Download code

applycharges_oedu.cpp

Adding custom parameters

Custom parameters can also be added to am application API when a built-in Options class does not contain all required parameters for the application.

The following code example is taken from OEDocking, and uses the OEPosit object to generate pose structures. In this example, the OEPositOptions is extended with an input, output, and receptor (i.e., the reference system). However, the number of poses to generate is not a built-in parameter into the OEPositOptions. This example shows how to add an additional parameter, for the number of poses, to be configured through the command line.

Listing 4: Adding custom parameters (POSIT with Multiple Receptors)

/*
(C) 2021 OpenEye Scientific Software Inc. All rights reserved.

TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
provided to current licensees or subscribers of OpenEye products or
SaaS offerings (each a "Customer").
Customer is hereby permitted to use, copy, and modify the Sample Code,
subject to these terms. OpenEye claims no rights to Customer's
modifications. Modification of Sample Code is at Customer's sole and
exclusive risk. Sample Code may require Customer to have a then
current license or subscription to the applicable OpenEye offering.
THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED.  OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall OpenEye be
liable for any damages or liability in connection with the Sample Code
or its use.
*/
#include "oebio.h"
#include "oechem.h"
#include "oedocking.h"
#include "oesystem.h"
#include "openeye.h"

class MyOptions : public OEDocking::OEPositOptions
{
public:
  MyOptions() : OEDocking::OEPositOptions()
  {
    OESystem::OEUIntParameter param1("-numPoses", 1);
    param1.AddLegalRange("1", "20");
    param1.SetBrief("Number of poses to generate");
    m_param1 = AddParameter(param1);
  }

  unsigned int GetNumConfs() const
  {
    unsigned int numConfs = 1;
    if (m_param1->GetHasValue())
      OESystem::OEStringToNumber(m_param1->GetStringValue(), numConfs);
    else
      OESystem::OEStringToNumber(m_param1->GetStringDefault(), numConfs);
    return numConfs;
  }

private:
  OESystem::OEParameter *m_param1;
};

int main(int argc, char **argv)
{
  MyOptions positOpts;
  OEChem::OERefInputAppOptions opts(positOpts, "PoseMolecules", OEChem::OEFileStringType::Mol3D,
                                    OEChem::OEFileStringType::DU, OEChem::OEFileStringType::DU, "-receptor");
  if (OESystem::OEConfigureOpts(opts, argc, argv, false) == OESystem::OEOptsConfigureStatus::Help)
    return 0;
  positOpts.UpdateValues(opts);

  OEChem::oemolistream ifs;
  if (!ifs.open(opts.GetInFile()))
    OESystem::OEThrow.Fatal("Unable to open %s for reading", opts.GetInFile().c_str());

  OEPlatform::oeifstream rfs;
  if (!rfs.open(opts.GetRefFile()))
    OESystem::OEThrow.Fatal("Unable to open %s for reading", opts.GetRefFile().c_str());

  OEPlatform::oeofstream ofs;
  if (!ofs.open(opts.GetOutFile()))
    OESystem::OEThrow.Fatal("Unable to open %s for writing", opts.GetOutFile().c_str());

  OEDocking::OEPosit poser(positOpts);

  OEBio::OEDesignUnit du;
  unsigned int count = 0;
  while (OEBio::OEReadDesignUnit(rfs, du))
  {
    if (!du.HasReceptor())
      OESystem::OEThrow.Fatal("Design Unit " + du.GetTitle() + " does not contain a receptor");
    poser.AddReceptor(du);
    count++;
  }
  if (count == 0)
    OESystem::OEThrow.Fatal("Receptor input does not contain any design unit");

  OESystem::OEThrow.Info("Number of conformers: %d", positOpts.GetNumConfs());
  OESystem::OEThrow.Info("Best receptor pose flag: %s", positOpts.GetBestReceptorPoseOnly() ? "true" : "false");

  OEChem::OEMol mcmol;
  while (OEChem::OEReadMolecule(ifs, mcmol))
  {
    OESystem::OEThrow.Info("posing %s", mcmol.GetTitle());
    OEDocking::OEPositResults results;
    unsigned int returnCode = poser.Dock(results, mcmol, positOpts.GetNumConfs());

    if (returnCode == OEDocking::OEDockingReturnCode::Success)
    {
      for (OESystem::OEIter<OEDocking::OESinglePoseResult> result = results.GetSinglePoseResults(); result; ++result)
      {
        OEBio::OEDesignUnit posedDU = result->GetDesignUnit();
        posedDU.SetDoubleData(poser.GetName().c_str(), result->GetProbability());
        OESystem::OEThrow.Info("Receptor used: %s pose probability: %f", posedDU.GetTitle().c_str(),
                               result->GetProbability());
        OEBio::OEWriteDesignUnit(ofs, posedDU);
      }
    }
    else
    {
      std::string errMsg = OEDocking::OEDockingReturnCodeGetName(returnCode);
      OESystem::OEThrow.Warning("%s: %s", mcmol.GetTitle(), errMsg.c_str());
    }
  }

  return 0;
}

Download code

GenerateMultiplePose.cpp

Defining new options class

Options classes, derived from OEOptions, can also be defined at the script level, and wrapped with the OESimpleAppOptions or OERefInputAppOptions, to build applications.

The following code example is taken from OEFF, and optimizes molecule conformations using a force field. In this example, a new Options class is defined to accept command line input for the force field.

Listing 5: Defining Options class (Optimize molecule)

/*
(C) 2020 OpenEye Scientific Software Inc. All rights reserved.

TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
provided to current licensees or subscribers of OpenEye products or
SaaS offerings (each a "Customer").
Customer is hereby permitted to use, copy, and modify the Sample Code,
subject to these terms. OpenEye claims no rights to Customer's
modifications. Modification of Sample Code is at Customer's sole and
exclusive risk. Sample Code may require Customer to have a then
current license or subscription to the applicable OpenEye offering.
THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED.  OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall OpenEye be
liable for any damages or liability in connection with the Sample Code
or its use.
*/

#include "openeye.h"
#include "oesystem.h"
#include "oechem.h"
#include "oebio.h"
#include "oeopt.h"
#include "oemolpotential.h"
#include "oemmff.h"
#include "oesmirnoff.h"
#include "oeff.h"

/////////////////////////////////////////////////////////////////////////////
// The following is an example to show how to evaluate energies            //
// of a small molecule and obtain various energy components.               //
/////////////////////////////////////////////////////////////////////////////

class FFOptions : public OESystem::OEOptions
{
public:
  FFOptions(std::string name = "FFOptions")
    : OESystem::OEOptions(name)
  {
    OESystem::OEStringParameter ffType("-ff", "parsley");
    ffType.AddLegalValue("parsley");
    ffType.AddLegalValue("mmff94");
    ffType.AddLegalValue("mmff94s");
    ffType.SetBrief("Fore field type");
    m_fftype = AddParameter(ffType);
  }

  FFOptions* CreateCopy() const { return new FFOptions(*this); }

  OEMolPotential::OEForceField* GetFF() const
  {
    std::string ff = m_fftype->GetStringValue();
    if (ff.empty())
      ff = m_fftype->GetStringDefault();

    if (ff == "mmff94")
      return new OEMolMMFF::OEMMFF();
    else if (ff == "mmff94s")
      return new OEMolMMFF::OEMMFF(OEMolMMFF::OEMMFF94sParams());
    else
      return new OEMolSmirnoff::OEParsley();
  }

private:
  OESystem::OEParameter* m_fftype;
};


int main(int argc, char* argv[])
{
  FFOptions ffOpts;
  OEChem::OESimpleAppOptions opts(ffOpts, "FFOptimize", OEChem::OEFileStringType::Mol3D,
    OEChem::OEFileStringType::Mol3D);
  if (OESystem::OEConfigureOpts(opts, argc, argv, false) == OESystem::OEOptsConfigureStatus::Help)
    return 0;
  ffOpts.UpdateValues(opts);

  OEChem::oemolistream ifs;
  if (!ifs.open(opts.GetInFile()))
    OESystem::OEThrow.Fatal("Unable to open %s for reading", opts.GetInFile().c_str());

  OEChem::oemolostream ofs;
  if (!ofs.open(opts.GetOutFile()))
    OESystem::OEThrow.Fatal("Unable to open %s for writing", opts.GetOutFile().c_str());

  std::unique_ptr<OEMolPotential::OEForceField> ff(ffOpts.GetFF());

  OEChem::OEMol mol;
  while (OEChem::OEReadMolecule(ifs, mol))
  {
    if (!ff->PrepMol(mol) || !ff->Setup(mol))
    {
      OESystem::OEThrow.Warning("Unable to process molecule: title = %s", mol.GetTitle());
      OEChem::OEWriteMolecule(ofs, mol);
      continue;
    }
    
    std::vector<double> vecCoords(3*mol.GetMaxAtomIdx());
    for (OESystem::OEIter<OEChem::OEConfBase> conf = mol.GetConfs(); conf; ++conf)
    {
      OESystem::OEThrow.Info("Molecule: %s Conformer: %d", mol.GetTitle(), conf->GetIdx()+1);     
      conf->GetCoords(&vecCoords[0]);

      // Calculate energy
      double energy = (*ff)(&vecCoords[0]);
      OESystem::OEThrow.Info("Initial energy: %f kcal/mol", energy);

      // Optimize the ligand
      OEOpt::OEBFGSOpt optimizer;
      energy = optimizer(*ff, &vecCoords[0], &vecCoords[0]);
      OESystem::OEThrow.Info("Optimized energy: %f kcal/mol", energy);
      conf->SetCoords(&vecCoords[0]);
    }
    OEChem::OEWriteMolecule(ofs, mol);
  }

  return 0;
}

Download code

FFOptimize.cpp