Skip to content

Developer: Adding file formats to QM Input Builder tool

Tony Schaefer edited this page Apr 5, 2021 · 3 revisions

File formats can be added to SEQCROW's QM Input File Builder using a Provider for SEQCROW's seqcrow_qm_input_manager ProviderManager. The name of your provider will be the same as the name of the file format on the tool. This page details the process of getting a new file format added to the tool.

Adding a Provider to bundle_info.xml

Specify providers with the name of the file format for the seqcrow_qm_input_manager. Below is an example for adding Gaussian, ORCA, and Psi4 files.

For more information on bundle info tags, see the official ChimeraX documentation.

<BundleInfo [your bundle attributes]>
    ... other stuff...
    <Providers manager="seqcrow_qm_input_manager">
        <Provider name="Gaussian"/>
        <Provider name="ORCA"/>
        <Provider name="Psi4"/>
    </Providers>
</BundleInfo>

Adding a Provider to Your Bundle API

A run_provider method should be added to your BundleAPI subclass. This should return an instance of a QMInputFileInfo subclass.

from chimerax.core.toolshed import BundleAPI

class _YourBundle_API(BundleAPI):
    @staticmethod
    def run_provider(session, name, manager, **kw):
        if manager is session.seqcrow_qm_input_manager:
            if name == "Gaussian":
                from SEQCROW.input_file_formats import GaussianFileInfo
                return GaussianFileInfo()
            elif name == "ORCA":
                from SEQCROW.input_file_formats import ORCAFileInfo
                return ORCAFileInfo()
            elif name == "Psi4":
                from SEQCROW.input_file_formats import Psi4FileInfo
                return Psi4FileInfo()

Specifying Available Options and Building the File

Available options, initial presets, and a method for constructing the input file are specified by various attributes and methods of your QMInputFileInfo subclass. Below is the definition of the QMInputFileInfo class with annotations describing each attribute and method.

The get_file_contents method will be given an AaronTools Theory instance for constructing the input file. For More information on this, see the AaronTools online documentation or the in-code documentation for AaronTools.theory.

Note that options for specifying the method and basis set names will have 'other' added to them automatically. With this in mind, only a few options should be listed for these so that users can quickly find what they are looking for instead of sifting through the dozens or hundreds of keywords that may be available in the QM software. If there is another option that a specific user utilizes regularly, they can type in the keyword by selecting 'other'. That keyword will be listed on a 'previously used' keyword table the next time that user selects the 'other' option.

class QMInputFileInfo:
    # name of program
    name = ""
    
    # preset settings for this software
    # dict with keys:
    #    theory - Theory instance
    #    use_other - bool, use Theory's kwargs attribute (assumed True if  
    #                 Theory.kwargs is not empty)
    #    hpmodes - bool, high-precision modes is checked
    #    raman - bool, raman intensities is checked (ignored unless theory 
    #            has a FrequencyJob)
    initial_presets = dict()
    
    # options to put in the 'previous' section of the stuff on the 'additional options' tab
    # although the user hasn't used these before, they can be used to build intuation
    # for how the KeywordOptions can be used to add whatever options they want
    initial_options = dict()
    
    # file filter for QFileDialog.getOpenFileName
    # if None, will be disabled when 'read checkpoint' is checked
    save_checkpoint_filter = None
    
    # file filter for QFileDialog.getSaveFileName
    # if None, will be disabled when 'read checkpoint' is unchecked
    read_checkpoint_filter = None
    
    # basis file filter
    basis_file_filter = None
    
    # filter for saving a file in this format
    save_file_filer = None
    
    # whether or not Raman intensities can be computed
    raman_available = False
    
    # whether or not high-precision vibrational mode displacement vectors can be requested
    # basically Gaussian-only option
    hpmodes_available = False
    
    # availale solvent models
    # if there are no solvent models, the widget will be disabled
    solvent_models = None
    
    # availale solvents
    # if all solvents are availale for all solvent models, can simply be a list
    # otherwise, should be a dict with the models as the keys
    solvents = None
    
    # availale methods - should be list
    # special methods:
    # SAPT - will show a layer widget for selecting SAPT type and defining monomers
    methods = []
    
    # availale empirical dispersion
    # if there are no dispersion methods, the widget will be disabled
    # will be prepended with "None", the corresponding Theory for which will
    # have no emp dispersion
    dispersion = None
    
    # availale integration grids
    # if there are none, the widget will be disabled
    # will automatically be prepended with "Default", the corresponding
    # Theory for which will have no grid
    grids = None
    
    # availale basis sets
    # will be appended with 'other', allowing the user to enter basis set info
    basis_sets = []
    
    # auxiliary basis set types
    aux_options = None
    
    # availale ECP's
    # if there are no ECP's, the ECP widget will be hidden
    # will be appended with "other", which allows users to enter ECP info
    ecps = None
    
    # misc. options
    # should be None ('additional options' tab will be disabled) or
    # a KeywordOptions subclass (not an instance, just the class)
    keyword_options = None

    def get_file_contents(self, theory):
        """
        returns file contents (str) and warnings (list of str)
        theory - AaronTools.theory Theory instance
        """
        raise NotImplementedError("cannot generate file contents for %s" % self.name)
    
    def get_local_job(self, session, name, theory, auto_update, auto_open):
        """
        get SEQCROW.jobs LocalJob instance for running a job on computer running ChimeraX
        session - ChimeraX session
        name - str, job name
        theory - AaronTools.theory Theory instance
        auto_update - bool, update the structure that was used to create the job once it is completed
        auto_open - bool, open the output file of the job when it completes
        """
        raise NotImplementedError("cannot run local jobs for %s" % self.name)

    def get_job_kw_dict(
            self,
            optimize,
            frequencies,
            raman,
            hpmodes,
            read_checkpoint,
            checkpoint_file,
    ):
        """
        get a keyword dictionary given the settings on the 'job details' tab
        optimize - bool, geometry optimization is checked
        frequencies - bool, frequency calculation is checked
        raman - bool, Raman intensities is checked
        hpmodes - bool, high-precision modes is checked
        read_checkpoint - bool, read checkpoint is checked
        checkpoint_file - str, path to checkpoint file
        """
        return dict()

Specifying Sections for Additional Options

The QMInputFileInfo.keyword_options attribute should be a SEQCROW.tools.input_generate.KeywordOptions subclass, which specifies the options that appear on the 'additional options' tab of the QM Input Builder tool. Below is an example of one such subclass:

class SimpleGaussianKeywordOptions(KeywordOptions):
    # items that appear on the 'position' option on the 'additional options' tab
    # the value is the key that will be used in the dictionary added to the
    # theory's kwargs attribute
    items = {
        'route': GAUSSIAN_ROUTE,
        'comment': GAUSSIAN_COMMENT,
    }

    # settings names for storing what's been used before
    previous_option_name = "previous_gaussian_options"
    last_option_name = "last_gaussian_options"

    @classmethod
    def get_options_for(cls, name, last, previous):
        """
        returns a widget for the specified option
        name - name of option (key in cls.items dictionary)
        last - dictionary or list of the options that were used the last
               time a file of this format was saved
        previous - dictionary or list of options that have been used before
        returns a widget to fill the tab
        """
        if name == "route":
            if last is None:
                last_dict = {}
            else:
                last_dict = last

            if previous is None:
                previous_dict = {}
            else:
                previous_dict = previous

            return TwoLayerKeyWordOption(
                "keywords",
                last_dict,
                previous_dict,
                "double click to add %s=(%s)",
                one_opt_per_kw=False
            )

        elif name == "comment":
            if last is None:
                last_list = []
            else:
                last_list = last

            if previous is None:
                previous_list = []
            else:
                previous_list = previous

            return OneLayerKeyWordOption(
                "comment",
                last_list,
                previous_list,
                multiline=True
            )

The get_options_for returns a widget to be placed on the 'additional options' tab what that item is selected. SEQCROW has two widgets that may be used in many cases:

  • OneLayerKeyWordOption - useful for inserting arbitrary lines
  • TwoLayerKeyWordOption - useful for when there are settings that can have a few different values

Both of these can be imported from SEQCROW.tools.input_generator. There are plans for adding support for customized widgets.