schrodinger.application.desmond.util module

Utility functions and classes

Copyright Schrodinger, LLC. All rights reserved.

class schrodinger.application.desmond.util.TestSuite(title)

Bases: object

A small and tight class designed for unit tests.

With this utility, unit test code can be written much faster than with the ‘unittest’ module.


Sets the title of the suite to ‘title’ and all internal counters to 0.


Performs testing of all test cases accumulated so far and print information indicating a particular case is passed or failed. The user can explicitly call this function as many times as like. Already tested cases will not be tested again. This means that the user can do something like this:

tsuite = TestSuite( 'util' )
tsuite << case1    # Testing of 'case1' will be performed    # Allowed, but meaningless since 'case1' will be tested again.

and so this:

tsuite << case1
tsuite << case1    # Allowed. The 'tsuite' takes the 2nd 'case1' as a new case and performs the testing.

The user usually does NOT need to call this function explicitly because when the ‘TestSuite’ object is about to be destructed, it will automatically check and test any untested cases.

class schrodinger.application.desmond.util.Counter(val=0)

Bases: object

This class was originally designed for the convenience of gridding widgets. For example, instead of writing a code like:

my_label1.grid( row = 1, sticky = W )
my_label2.grid( row = 2, sticky = W )
my_label3.grid( row = 3, sticky = W )

we can avoid the hardcoding (‘row = 1’, etc., which is generally bad and hard to maintain) using this class. The improved code will look like:

row_index = Counter()
my_label1.grid( row = row_index.val, sticky = W )
my_label2.grid( row = row_index.val, sticky = W )
my_label3.grid( row = row_index.val, sticky = W )

which is equivalent to the above code, but generally easier to write and modify. The trick is that the property ‘val’, when read, will return the current value of the internal counter and then increment the counter (not the returned value) by 1.

If the user just wants to get the current value of the counter but not want to change it, s/he can do either one of the two:

  1. Use the 'va_' property,

  2. Explicitly convert the object to ‘int’.


Constructs the object. One can provide a value ‘val’ to initialize the internal variable. For example:

row_index = Counter( 2 )

will let the counter start from 2, instead of 0 (default value).


Resets the counter to ‘val’.

property val

Readonly. When read, this returns the value of the current count and then increment the count by 1. The incrementation does not affect the returned value.

property va_

Readonly. When read, this returns the value of the current count without changing the internal state whatsoever of the object.

schrodinger.application.desmond.util.remove_file(basename: str, prefix: Optional[List[str]] = None, suffix: Optional[List[str]] = None)

Tries to delete files (or dirs) whose names are composed by the given basename, a list of prefixes (prefix), and a list of suffixes (suffix). No effects if a file (or dir) does not exist.

schrodinger.application.desmond.util.get_basename_if_relative_path(file_path: str) str

Check if the supplied path is relative, and return its basename (which is what jobserver copies over) if so. Otherwise, return the given path.


file_path – the path to check


the file basename if it is relative, or the input path

schrodinger.application.desmond.util.write_n_ct(fname, struc)

Writes a list of CTs to a file with the name as given by ‘fname’.

The CTs in the output file are in the same order as in the list. The list can contain None elements, which will be ignored. This function has no effect if the ‘struc’ is an empty list or contains only Nones.

!!!DEPRECATED!!! Use struc.write_structures instead.


Changes the current directory to the one of the name ‘dir_name’. If ‘dir_name’ is ‘..’, then it will change to the parent directory, and this is is done in a portable way.

schrodinger.application.desmond.util.parent_dir(dir_name, up=1)

Returns the parent directory name.


up – This should be a non-negative integer value indicating the parent along the path. Default value is 1, indicating the immediate parent. Value 2, for example, indicates the parent of the immediate parent directory.

schrodinger.application.desmond.util.relpath(xpath, refpath=None)

Given two paths (‘xpath’ and ‘refpath’), returns the relative path of ‘xpath’ with respect to ‘refpath’.

Both ‘xpath’ and ‘refpath’ can be relative or absolute paths, and ‘refpath’ defaults to the current directory if it is not provided.

Creates a symbolic link on the current directory to a file as given by ‘src_fname’.

This differs from ‘os.symlink’ in that this function creates a symbolic link using a relative path. Also if there is already a file with the same name as ‘des_fname’, this function will try to delete the file and then create symbolic link. An exception will be raised, if this attemp fails.

  • src_fname – The name of the file to link to.

  • def_fname – The name to use for the symbolic link.

schrodinger.application.desmond.util.is_subdir(xpath, refpath=None)

Given two paths (‘xpath’ and ‘refpath’), returns True if ‘xpath’ is a direct or indirect subdirectory of ‘refpath’. Also returns True if ‘xpath’ and ‘refpath’ are the same.

Both ‘xpath’ and ‘refpath’ can be relative or absolute path, and ‘refpath’ defaults to the current directory if it is not provided.

schrodinger.application.desmond.util.append_comment(fname, comment)

Appends a string ‘comment’ to a file ‘fname’. A char ‘#’ will be automatically added to the head of the string.


comment – A string or a list of strings. If it is a list of strings, each string will be appended as a separate comment.

schrodinger.application.desmond.util.random_string(n, char_pool='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890')

Returns a random string with ‘n’ chars. The ‘n’ chars will be taken from a pool of chars as given by ‘char_pool’.


Returns the login name if it can be found, or otherwise ‘unidentified_user’.

Note: ‘os.getlogin’ seems to have a bad dependency on the terminal in use, and as such exception can be thrown when the terminal does not provide the requested login information. This function is to solve this problem by trying three different ways to get the login name, and if all ways failed it will return a valid string that tells the login name can not be identified.

schrodinger.application.desmond.util.expand_macro(s, macro_dict)

Replaces the macros in the string ‘s’ using the values given by the macro dictionary ‘macro_dict’. The expanded string will be returned.

Macro conventions:
  • All macros should start with a single ‘$’, followed by capital letters, e.g., “$JOBNAME”, “$USERNAME”.

  • Optional macros should be bracketed by ‘[]’, e.g., “myjob[_lambda$LAMBDANO]”, where “$LAMBDANO” is an optional macro.

  • If optional macros are not expanded, the bracketed part of the string will be discarded.

  • Macro values should not contain the following chars: ‘[’, ‘]’, and ‘$’.

schrodinger.application.desmond.util.fatal(msg: str) None
schrodinger.application.desmond.util.ensure_file_exists(fname: str)

Ensure that the file exists and is not empty.


SystemExit – If the file is not found or is empty.

schrodinger.application.desmond.util.verify_file_exists(fname_pattern: str, exit_on_error=False) str

Verifies that a single file/path matching fname_pattern actually exists and its size is not zero, and returns the actual file name. If the verification failed, IOW, no files found or multiple files found or the file is empty, raises an IOError (or SystemExit' if `exit_on_error is true).

schrodinger.application.desmond.util.verify_traj_exists(fname_pattern: str) str

Verifies that one and only one trajectory file (which may be a regular file or a directory, depending on the trajectory format) matching the given file name pattern fname_pattern actually exists, and returns the name of the trajectory file if it’s found, or raises an IOError otherwise.

fname_pattern follows the glob syntax. If the pattern doesn’t contain a supported trajectory extension name, it will be treated as the pattern of the base name.

schrodinger.application.desmond.util.time_duration(start_time, end_time, scale=1)

Given the start time and the end time, returns a string that says the duration between the two time points in the format of ‘xh y’ z”’, where ‘x’, ‘y’, and ‘z’ are hours, minutes, and seconds, respectively.

  • start_time – A time in seconds since the Epoch (i.e. a value as returned by the ‘time.time()’ function).

  • end_time – A time in seconds since the Epoch (i.e. a value as returned by the ‘time.time()’ function).

Returns a tuple with the following elements:
  • 0 - the exec dir

  • 1 - the lib dir

  • 2 - the version number

  • 3 - the platform

All elements are strings.

schrodinger.application.desmond.util.unique(seq: Iterable)

Iterates over a given sequence seq in the same order. If there are duplicate elements, only the first occurence is preserved. For example:

[1, 2, 3, 3, 4, 3, 4, 0] ==> [1, 2, 3, 4, 0]

This function requires that all elements in seq are hashable.

schrodinger.application.desmond.util.get_traj_filename(basename: str) Optional[str]

Return (chain, resnum, inscode) from residue name in the form <chain>:<resnum><inscode> Input should be in the form <chain>:<resnum><inscode> <chain> is the chain id or a _ or no character if space <resnum> is the residue number (possibly negative) <inscode> is the pdb insertion code Examples A:12 :23A B:-1 _:12

schrodinger.application.desmond.util.parse_edge_file(fname: str) List[Tuple[str, str]]
  1. An edge is identified by its ID, which is a string of the two node IDs separated by ‘_’.

  2. Each node ID can be either a full ID or a short ID.

  3. Each line in the .edge file should contain at most 1 edge ID.

  4. Lines containing only white spaces are allowed, and they will be ignored by the parser.

  5. A comment is a string that starts with ‘#’ and ends with ‘


and it will be ignored by the parser.


A list of edge IDs from the parsed edge file.

schrodinger.application.desmond.util.parse_ligand_file(fname: str) List[str]

Parse a ligand file with the following format:

  1. On each line, a ligand is identified by the hash id

  2. Lines containing only white spaces are allowed, and they will be ignored by the parser.

  3. Each line in the .ligand file should contain at most 1 ligand ID.

  4. Lines containing only white spaces are allowed, and they will be ignored by the parser.

  5. A comment is a string that starts with ‘#’ and ends with ‘n’, and it will be ignored by the parser.


A list of structure hash ids.

schrodinger.application.desmond.util.write_ligand_file(fname: str, cts: List[schrodinger.structure._structure.Structure]) None

Given a list of structures, write a file containing ligand hash ids

  • fname – Path for output.

  • cts – List of structures.

schrodinger.application.desmond.util.str2hexid(s: str, full_id=False) str

Returns a unique hex code for the given string s. The chances of returning the same hex code for different values of s is low enough (though not zero in principle), the returned hex code can serve as an ID of the input string.

By default, the returned hex code is 7 digit long, which is only the initial part of the full code in 40 digits. To get the latter, set the argument full_id=True.

schrodinger.application.desmond.util.check_command(cmd: List, *args, **kwargs)

Check the command line arguments against args for positional arguments and kwargs for keyword arguments.

For flags like -multiword-flag, the corresponding keyword in kwargs is multiword_flag . This function by default does NOT distinguish a -multiword-flag from a -multiword_flag. Both forms are considered valid flags. If you want to force a dash flag, prefix the keyword with "DASHFLAG_" , e.g., “DASHFLAG_multiword_flag”; to force a underscore flag, prefix the keyword with "UNDERSCOREFLAG_"

For user’s convenience, one can use any types of values in args and kwargs. The value will be converted to str before checking if it exists in cmd. For example, you can use this key-value pair in kwargs: maxjob=1, which is equivalent to maxjob='1'.

For keyword arguments that take multiple values, e.g., “-d stage_1-out.tgz -d stage_2-out.tgz”, the values in kwargs should be specified in a list, e.g., d=["stage_1-out.tgz", "stage_2-out.tgz"].

For keyword arguments that take NO values, use None as the value in kwargs.



Must satisfy the following requirements:

  1. All argument flags should be single dash flags.

  2. If an argument that takes a single value but specified multiple times in cmd the right-most specification is in effect.


AssertionError – If any arguments as specified by args and kwargs are NOT found in cmd.

schrodinger.application.desmond.util.commandify(raw_cmd: List) List[str]

A subprocess command is a list of strings. This is often not the most convenient data structure for composing the command. For example, if you have numbers in the command, you have to convert them into strings; if the value of an argument is None, instead of putting the string “None” into the command, you want to drop the argument altogether.

This function is to make command composition a bit less boiler-plated, by providing some grammars:

  1. A “raw command” is one that can be properly processed by this function to return a subprocess command.

  2. A raw command is a list of arbitrary types of objects.

  3. For positional arguments, they should be direct and string-convertible elements in the raw command. If an element is None it will be removed from the returned command.

  4. A keyword argument should be specified as a list in the raw command. The first element of the list should be the flag, which again can be of any string-convertible type. The rest elements should be the values. If any of the values is None, this keyword argument will be removed from the returned command.

  5. A switch argument (which has no values following the flag) is similar to the keyward argument, except that it should have one and only one value, of the boolean type. If the value is True, the flag will be added into the returned command; otherwise it will be removed from there.


Determines if the given structure was marked by the System Build panel(s) to indicate that the custom OPLSDIR in Maestro preferences should be used.


st (structure.Structure) – structure whose properties are to be queried


whether to use the Maestro preference custom OPLSDIR

Return type


schrodinger.application.desmond.util.gz_fname_if_exists(fname: str)
schrodinger.application.desmond.util.copy_and_compress_files(src_dir: str, dest_dir: str, compress_pattern=None)

Copy the files from src_dir to dest_dir, optionally compressing a subset of files.


compress_pattern – Optional, files that match the pattern will be gzip compressed and renamed to have a .gz extension.

schrodinger.application.desmond.util.get_leg_name_from_jobname(jobname: str) str
schrodinger.application.desmond.util.get_leg_type_from_jobname(jobname: str) str
schrodinger.application.desmond.util.get_msj_filename(jobname: str, leg: Optional[schrodinger.application.desmond.constants.FepLegTypes] = None, protocol: Optional[schrodinger.application.desmond.constants.SIMULATION_PROTOCOL] = None, extend: Optional[bool] = False) str

Return the standardized .msj filename as a string.

schrodinger.application.desmond.util.is_dummy_structure(st: schrodinger.structure._structure.Structure) bool

Return whether the structure is a dummy structure.

constants.DUMMY_LIGAND is the current way to mark a structure but we also check for the deprecated constants.ABFEP_DUMMY_LIGAND.

schrodinger.application.desmond.util.make_structure_dummy(st: schrodinger.structure._structure.Structure) schrodinger.structure._structure._StructureAtom

Mark structure as the dummy and add a dummy atom. This is needed for FEP simulations which use the Graph format but don’t have traditional lambda 0 and lamdba 1 inputs (e.g. Absolute Binding, Solubility)

schrodinger.application.desmond.util.predict_memory_utilization(fep_type: schrodinger.application.desmond.constants.FEP_TYPES, num_atoms: int, num_windows: int) Tuple[int, int]

Predict the cpu and gpu memory utilization in MB for an fep job.

schrodinger.application.desmond.util.get_ligand_rmsd(asl_receptor: str, asl_ligand: str, msys_model: msys.System, cms_model: cms.Cms, trj: List[traj.Frame], ref_ct_prop_names: Optional[List[str]] = None) List[float]

Measure RMSD of the ligand aligned on the receptor’s input coordinates.

  • asl_receptor – ASL query specifying the receptor atoms, to which the ligand will be aligned for the RMSD calculation

  • asl_ligand – ASL query specifying the ligand, for which the RMSD will be calculated

  • msys_model – defines the system structure and atomic mapping

  • cms_model – defines the system structure and atomic mapping

  • trj – trajectory to be analyzed

  • ref_ct_prop_names – property names for the reference coordinates, passed to the get_reference_ct() call. If None, use the default value of the get_reference_ct() function. See docstring of get_reference_ct() for more usage information.


a list of RMSD values for each timestep in the trajectory