Becksteinlab / GromacsWrapper

GromacsWrapper wraps system calls to GROMACS tools into thin Python classes (GROMACS 4.6.5 - 2024 supported).
https://gromacswrapper.readthedocs.org
GNU General Public License v3.0
170 stars 53 forks source link

AttributeError: module 'gromacs.tools' has no attribute 'Grompp' when using `gmx_mpi` only #262

Open oliviertrottier opened 1 year ago

oliviertrottier commented 1 year ago

Hi, I am fairly new to using Gromacs and stumbled upon the gromacswrapper package, which I find super useful and well documented. I wanted to use some of the cookbook recipes, but I am not able to load cbook. Here is the error message I get when I try to import cbook from gromacs:

/home/olivier/anaconda3/envs/gmx-sim/lib/python3.11/site-packages/gromacs/cbook.py:229: GromacsImportWarning: Failed to define a number of commands in gromacs.cbook. Most likely the Gromacs installation cannot be found --- set GMXRC in ~/.gromacswrapper.cfg or source GMXRC directly
  warnings.warn(msg, category=GromacsImportWarning)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[396], line 1
----> 1 from gromacs import cbook

File ~/anaconda3/envs/gmx-sim/lib/python3.11/site-packages/gromacs/cbook.py:668
    659             self.cleanup()
    662 # Working with topologies
    663 # -----------------------
    664 
    665 # grompp that does not raise an exception; setting up runs the command to get the docs so
    666 # we only want to do this once at the module level and not inside a function that can be called
    667 # repeatedly
--> 668 grompp_warnonly = tools.Grompp(failure="warn")
    669 # grompp_warnonly.__doc__ += "\n\ngrompp wrapper that only warns on failure but does not raise :exc:`GromacsError`"
    672 def grompp_qtot(*args, **kwargs):

AttributeError: module 'gromacs.tools' has no attribute 'Grompp'

Here are my configurations in ~./gromacswrapper.cfg.

[Gromacs]
GMXRC = /usr/local/gromacs/bin/GMXRC
tools = gmx_mpi

I only have one gromacs installation, which is the _mpi version. I noticed that all the attributes are suffixed with _mpi. That seems to be the cause of the problem since python cannot find the non-suffixed version. Is there a way to use the gromacswrapper classes in a gromacs-command-agnostic way? I would like to write one codebase that I can use for multiple gromacs installations without having to change the suffix of all the attributes that I am using.

orbeckst commented 1 year ago

I can't remember if your use case (only gmx_mpi) came up before. But I am a bit surprised that tools.Grompp was not defined. It should have been the equivalent of gmx_mpi grompp (or at least that's how it's supposed to work!).

Are you able to run (in the shell)

$ . /usr/local/gromacs/bin/GMXRC
$ gmx_mpi grompp -h

If that doesn't work then tools.Grompp will not be created.

oliviertrottier commented 1 year ago

I was able to run these lines without problems but the loading problem was still happening.

I looked at the tools.py file to understand how the commands are loaded and it seemed it's just reading the output of {driver} -quiet help command to define the commands from the defined driver. That was working fine and all the commands were loading properly.

However, I noticed when these commands are parsed, suffix is appended only when the append_suffix option is set to to true in the configs file. I then added append_suffix = false in my gromacswrapper.cfg and it worked! I can now load cbook and all the methods are not suffixed with _mpi. I am guessing that will only work if only one driver is specified in the tools option.

Here are the relevant lines of tools.py starting from line 435:

    append = config.cfg.getboolean("Gromacs", "append_suffix", fallback=True)

    tools = {}
    for driver in drivers:
        suffix = driver.partition("_")[2]
        try:
            out = subprocess.check_output([driver, "-quiet", "help", "commands"])
            for line in out.splitlines()[5:-1]:
                line = str(
                    line.decode("ascii")
                )  # Python 3: byte string -> str, Python 2: normal string

                if len(line) > 4:
                    if (line[4] != " ") and (" " in line[4:]):
                        name = line[4 : line.index(" ", 4)]
                        fancy = make_valid_identifier(name)
                        if suffix and append:
                            fancy = "{0!s}_{1!s}".format(fancy, suffix)
                        tools[fancy] = tool_factory(fancy, name, driver)
orbeckst commented 1 year ago

I am glad that you managed to get it working.

Do you have a suggestion how we could improve either the code or the documentation? If you want to update https://github.com/Becksteinlab/GromacsWrapper/blob/main/doc/sphinx/source/configuration.txt then that would be especially helpful.

oliviertrottier commented 1 year ago

Like I mentioned in my first post, it would be useful to be able to change the base Gromacs command dynamically with a function in gromacs.tools. I looked at gromacs.tools and it seems it would be rather simple to do that. All Gromacs commands are subclassing Command which defines the base Gromacs command through the driver attribute. The function could simply loop through all GromacsCommand instances defined gromacs.tools which are not suffixed and redefine their driver attribute with the user input. For example, if set_driver is such command, one could then use the same codebase to use gmx_mpi on a cluster and use gmx when running on another computer without an mpi installation:

if using_cluster:
   gromacs.tools.set_driver('gmx_mpi')
else:
   gromacs.tools.set_driver('gmx')

# Run set of commands using non-suffixed tools...
orbeckst commented 1 year ago

I doubt that I myself will have time to do much on this issue in the near future. If you want to contribute code then please do and open a PR. I am happy to review it (just ping me with @orbeckst to get my attention).

oliviertrottier commented 1 year ago

Thank you for offering the opportunity to contribute. I will be happy to write some code to hopefully make the package more portable. I am currently in a rush right now to write another code for my research. I'll ping you when I open the PR.