schrodinger.application.desmond.multisim.parser module

Functions to parse MSJ files

Copyright Schrodinger, LLC. All rights reserved.

class schrodinger.application.desmond.multisim.parser.Msj(*args, **kwargs)

Bases: schrodinger.application.desmond.ana.ArkDb

The use of this class is very similar to ana.ArkDb. Some terminologies and syntax defined in ana.ArkDb are not repeated here. So if you are unfamiliar with ana.ArkDb, you are encouraged to read the docstrings there. Below are examples to demonstrate and explain the use of this class:

msj = parse(msj_fname)

# 1. To read the time setting in stage 3: msj.get(“stage[2].simulate.time”) msj.get(“stage[-1].simulate.time”) # - stage[2] in the key corresponds to stage 3 because the stage index in # the code is always zero based. # - Negative stage indices are supported and are of the same meaning as in # Python. # - The stage type (here it is simulate) is always needed in the key. This # is good for preventing some kind of index errors, and is very useful # when you do NOT care about the index (see below).

# 2. To read the time setting in the first lambda_hopping stage: msj.get(“stage[i].lambda_hopping.time”)

# 3. Default value for non-existing setting: msj.get(“stage[i].lambda_hopping.phony_setting”, 1000)

# 4. To change the value of a setting: msj.put(“stage[i].lambda_hopping.time”, 1000)

# 5. To append a new stage fep_analysis and use the stage’s default # settings: msj.put(“stage[$].fep_analysis”, {})

# 6. To insert a new stage build_geometry to be the 2nd stage: msj.put(“stage[@]1.build_geometry”, {})

# 7. To insert a new stage assign_forcefield with custom settings: msj.put(“stage[@]2.assign_forcefield”,

sea.Map(```
hydrogen_mass_repartition = off make_alchemical_water = on```))

# Note that it’s necessary to convert the settings in the form of a string # into a sea.Map.

# 8. To delete the first “simulate” stage: msj.delete(“stage[i].simulate”)

# 9. To delete the “simulate” stage whose title reads “production”: msj.delete(“stage[i].simulate”, matches=”title=production”)

# 10. To delete all “simulate” stages: msj.delete(“stage[*].simulate”)

# 11. To access a particular stage, use the syntax: # msj.stage[: msj.stage[10].simulate.time.val += 1000

# 12. Support for shorthand keys: msj.put(“lambda_hopping.phony_setting”, 2000) msj.get(“lambda_hopping.phony_setting”) msj.put(“[@]1.build_geometry”, {}) # - If the key starts with the stage type name, it will be assumed to be # prefixed with ‘stage[i].’. In other words, the first instance of the # stage type will be operated on. # - The key can start with ‘[i]’, ‘[*]’, ‘[@]’, and ‘[$]’, and it will be # automatically prefixed with ‘stage’.

# 13. Find the index of the first “simulate” stage: first_simulate_index = msj.find_stage(“simulate”)[0].STAGE_INDEX # - find_stage always returns a tuple of the found stages, that’s why # [0] is used to get the first and the only found “simulate” stage. # - STAGE_INDEX gives the stage’s index in the MSJ file. Note that the # index is one-based.

# 13. Find the index of the last “trim” stage: last_trim_index = msj.find_stage(“[*].trim”)[-1].STAGE_INDEX # - Here we use the key “[*].trim” to find all “trim” stages and then select # the last one with [-1]. # - We don’t use the key “trim” (remember it’s a short hand of “[i].trim”), # because it means to get the first “trim” from the beginning of the stage # list.

# 14. Find the index of the second “simulate” stage: second_simulate_index = msj.find_stage(“[*].simulate”)[1].STAGE_INDEX

# 15. Find the index of the “simulate” stage whose “title” parameter is set # to “production”. production_index = msj.find_stage(“[*].simulate.title”

picker=lambda title: (title.parent() if title.val == “production”
else None)

)[0].STAGE_INDEX

# - With the key “[*].simulate.title”, we find the titles of all simulate # stages, then we use a lambda function as the picker to select the # stage whose title is “production”. Note that the “parent” of the “title” # parameter is the stage, which is what title.parent() gives.

(Feel free to add more examples)

__init__(*args, **kwargs)

Initialize self. See help(type(self)) for accurate signature.

stage
get(key: str, *args, **kwargs)

Gets a value keyed by key. Note that None is a normal return value and does NOT mean that the key was not found.

Raises:
  • CompositeKeySyntaxError – if key has a syntax error. You normally should NOT catch this exception, because this means your code has a syntactical error.
  • ArkDbGetError – if key is not found in the database. You can optionally change raising the exception to returning a default value by specifying the “default” argument.

Explanation on key’s value: - The value is generally a composite key like “a.b.c[1].d”, where “a”,

“b”, “c”, “[1]”, and “d” are the subkeys or array-indices at each hierarchical level.
  • For array indices, sometimes the exact number is unknown a priori, e.g., “ResultLambda0.Keywords[<number>].ProtLigInter”, where the <number> cannot be specified in the source code. For cases like this, we have to iterate over the “ResultLambda0.Keywords” list and find “ProtLigInter” by matching the keyword. Note that it’s possible (at least in principle) that there may be multiple matching elements.

  • In order to express the above indexing ideas, we introduce four new syntax components here: - [i] Iterates over elements in the list and returns the first

    matching element. For getting, putting, finding, and deleting.

    • [*] Iterates over elements in the list and returns a tuple of all
      matching elements. Only for getting, finding, and deleting.
    • [$] Insert at the end of the list. Only for putting.
    • [@] Similar to [$] except that this is for insertion into an
      arbitrary position in the list. This is to be used with a number immediately followed, e.g., [@]123, and the number specifies the position in the list. Only for putting.

    We may call these meta-indices. Examples: - “ResultLambda0.Keywords[i].ProtLigInter”

    • Gets the first “ProtLigInter” data.
    • “ResultLambda0.Keywords[*].ProtLigInter” - Gets all “ProtLigInter” data, and returns a tuple.

    • “ResultLambda0.Keywords[@]0.ProtLigInter” - Inserts a new “ProtLigInter” data at “ResultLambda0.Keywords[0]” - Note the difference from using “ResultLambda0.Keywords[0]”, which

      is to change the existing data.

    • “ResultLambda0.Keywords[$].ProtLigInter” - Appends a new “ProtLigInter” data to “ResultLambda0.Keywords”.

put(key: str, *args, **kwargs)

Puts a value associated with the given key into this database. value can be either of a scalar type, or of list, or an empty dict ({}), or of sea.Sea. key can be a composite key, see the docstring of ArkDb.get for detail.

Raises:
  • CompositeKeySyntaxError – if key has a syntax error. You normally should NOT catch this exception, because this means your code has a syntactical error.
  • ArkDbPutError – if putting failed.
delete(key: str, *args, **kwargs)

Deletes a given key and the value from the database. If the key is not found, ArkDbDelError will be raised unless ignore_badkey is True.

matches, if specified, provides one or more key-value pairs for checking on the value. If and only if all key-value pairs are found in the value, the key and the value will be deleted from the database. Each key-value pair is a string in the format of “<key>=<value>”. Note that the key and the value are connected by a single “=” symbol, no spaces allowed in the connection. Key is in the extended standard composite format (see the docstring of the ArkDb class above). Value is in the ARK format (note that spaces are allowed in the value). The value part is optional, when it’s missing, the “=” symbol should be absent as well, and this function will only look for the key in db and disregard the value.

Examples:

db.delete("a.b.c")
db.delete("a.b.d[i].e")
db.delete("a.b.d[i]", matches="e")
db.delete("a.b.d[i]", matches=("e=5", "h=10"))
find(key: str, *args, **kwargs)

Finds the given key and returns the corresponding data as a ForEachDo object. The ForEachDo object allows to iterate over the found data, each as a new ArkDb (or its subclass) object. It also allows us to concatenate operations on the found data.

Example:

db.find("stage[*].simulate").put("ensemble", "NVT")
# Resets all simulate stages' "ensemble" parameter's value to "NVT".

If the key is not found, this method will return () (i.e., empty tuple).

Parameters:picker

This is to cherry-pick the found data. The follow types or values are supported: - None All found data will be returned. - int Among the found data, a single datum as indexed by

picker will be returned. The index is zero-based.
  • List[int] Among the found data, multiple data as indexed by
    picker’s elements will be returned. The indices are zero-based.
  • Callable picker will be called on each found data, and the
    results will be filter-ed and returned.

Example:

db.find("stage[*].task", picker=1)             .put("set_family.simulate.temperature", 300)
# Mutates the second "task" stage.

db.find("stage[*].simulate.restrain", picker=lambda x: x.parent())             .put("temperature", 400)
# For any simulate stages with "restrain" setting, resets temperature
# to 400.
find_stages(key: str, *args, **kwargs) → Tuple[schrodinger.utils.sea.Map]

Similar to find, but to return a tuple of found stages. If no stages are found, this function returns an empty tuple. Examples:

# To get all simulate stages: simulate_stages = msj.find_stages(“[*].simulate”) for stage in simulate_stages:

print(stage.STAGE_INDEX)

# To get the first simulate stage: first_simulate_stage = msj.find_stages(“simulate”)[0] print(first_simulate_stage.STAGE_INDEX)

# To get the first simulate stage with “time = 200”: simulate = msj.find_stages(“[*].simulate.time”,

picker=lambda x: (x.parent() if x.val == 200
else None))[0]

print(simulate.STAGE_INDEX)

STAGE_INDEX gives the index of the stage in the MSJ file. Note that the index is one based. Also, STAGE_INDEX is an attribute (as opposed to a key-value pair) of the returned sea.Map objects.

val
write(fname: str)
schrodinger.application.desmond.multisim.parser.parse(fname=None, string=None) → schrodinger.application.desmond.multisim.parser.Msj

Parses a file or a string, and returns an Msj object. Either fname or string must be set, but not both.