"""
Facilities for detecting type of model systems.
Copyright Schrodinger, LLC. All rights reserved.
"""
import os
from schrodinger import structure
from schrodinger.application.desmond import cms
from schrodinger.application.desmond import constants
from schrodinger.application.desmond import struc as cst
from schrodinger.structutils import analyze
[docs]class UnknownAppError(Exception):
pass
[docs]class UnknownRuleError(Exception):
pass
[docs]class DetectionError(Exception):
pass
class _TypeBaseMeta(type):
def __init__(cls, name, bases, dict):
cls.typer_cls[cls.NAME] = cls
[docs]class TyperBase(object, metaclass=_TypeBaseMeta):
typer_cls = {}
NAME = "generic"
RULE = {}
STRING = {
None: "",
}
[docs] @staticmethod
def prepare(*arg, **kwarg):
pass
[docs] @staticmethod
def check_rule(systype, rule):
return True
[docs] @staticmethod
def detect(struc, filename):
pass
[docs] @staticmethod
def has_rule(rule):
return False
[docs] @staticmethod
def get_trait(struc):
return []
[docs]class DesmondTyper(TyperBase):
NAME = "desmond"
# Type codes:
UNKNOWN = 0
MATURE_REGULAR = 101
MATURE_FEP = 102
MATURE_AFEP = 103
RAW_REGULAR = 201
RAW_FEP = 202
RAW_AFEP = 203
STRING = {
MATURE_REGULAR: "a mature model system created for regular molecular dynamics simulations",
MATURE_FEP: "a mature model system created for FEP calculations",
MATURE_AFEP: "a mature model system created for absolute FEP calculations",
RAW_REGULAR: "a raw geometry for regular molecular dynamics simulations",
RAW_FEP: "raw geometries created for FEP calculations",
RAW_AFEP: "raw geometries created for absolute FEP calculations",
}
RULE = {
"mature_regular": [
MATURE_REGULAR,
MATURE_AFEP,
],
"mature_fep": [
MATURE_FEP,
MATURE_AFEP,
],
"mature_afep": [MATURE_AFEP,],
"mature": [
MATURE_REGULAR,
MATURE_FEP,
MATURE_AFEP,
],
"raw_regular": [
RAW_REGULAR,
RAW_AFEP,
],
"raw_fep": [
RAW_FEP,
RAW_AFEP,
],
"raw_afep": [RAW_AFEP,],
"raw": [
RAW_REGULAR,
RAW_FEP,
RAW_AFEP,
],
"regular": [
MATURE_REGULAR,
MATURE_AFEP,
RAW_REGULAR,
RAW_AFEP,
],
"fep": [
MATURE_FEP,
MATURE_AFEP,
RAW_FEP,
RAW_AFEP,
],
"afep": [
MATURE_AFEP,
RAW_AFEP,
],
"auto": [
MATURE_REGULAR,
MATURE_FEP,
MATURE_AFEP,
RAW_REGULAR,
RAW_FEP,
RAW_AFEP,
],
}
[docs] @staticmethod
def detect(struc, filename):
"""
"""
try:
struc = cms.check_sanity(struc)
type_ = cms.get_model_system_type(struc[1:])
type_map = {
constants.SystemType.OTHER: DesmondTyper.MATURE_REGULAR,
constants.SystemType.ALCHEMICAL: DesmondTyper.MATURE_FEP,
constants.SystemType.BINDING: DesmondTyper.MATURE_AFEP
}
return type_map[type_]
except (
KeyError,
ValueError,
):
pass
# DesmondTyper.RAW_FEP?
env_ct = []
ref_ct = []
mut_ct = []
for st in struc:
try:
if (st.property[constants.FEP_FRAGNAME] == "none"):
ref_ct.append(st)
else:
mut_ct.append(st)
except KeyError:
env_ct.append(st)
if (ref_ct != [] and mut_ct != []):
for st in ref_ct + mut_ct:
try:
a = st.atom[1].property[constants.FEP_SUBST]
except KeyError:
return DesmondTyper.RAW_FEP
if (len(ref_ct) > 1):
raise TypeError(
"More than 1 reference CT were defined for perturbations")
return DesmondTyper.RAW_FEP
elif (ref_ct == [] and mut_ct != []):
raise TypeError("Reference CT not found")
# DesmondTyper.RAW_AFEP?
for st in struc:
atom_property = list(st.atom[1].property)
if (constants.FEP_ABSOLUTE_LIGAND in atom_property):
if (constants.FEP_ABSOLUTE_ENERGY in atom_property):
lig_atom = set(
analyze.evaluate_asl(
st, f"atom.{constants.FEP_ABSOLUTE_LIGAND} 1"))
ene_atom = set(
analyze.evaluate_asl(
st, f"atom.{constants.FEP_ABSOLUTE_ENERGY} 1"))
if (lig_atom != ene_atom):
raise TypeError(
f"Atom properties \"{constants.FEP_ABSOLUTE_ENERGY}\" and \"{constants.FEP_ABSOLUTE_LIGAND}\" does NOT match with "
"each other.\n"
"The system might be setup for ligand-binding FEP calculations but atom "
"properties were not set correctly for that purpose."
)
if (lig_atom != set()):
return DesmondTyper.RAW_AFEP
else:
raise TypeError(
f"Atom property \"{constants.FEP_ABSOLUTE_ENERGY}\" is missing while \"{constants.FEP_ABSOLUTE_LIGAND}\" is defined.\n"
"The system might be setup for ligand-binding FEP calculations but atom properties "
"were not set correctly for that purpose.")
return DesmondTyper.RAW_REGULAR
[docs] @staticmethod
def has_rule(rule):
return rule in DesmondTyper.RULE
[docs] @staticmethod
def check_rule(systype, rule):
return systype in DesmondTyper.RULE[rule]
[docs] @staticmethod
def check_system(struc, filename, rule):
return DesmondTyper.check_rule(DesmondTyper.detect(struc, filename),
rule)
[docs] @staticmethod
def get_trait(struc):
trait = []
for st in struc:
try:
if (st.property[constants.CT_TYPE] ==
constants.CT_TYPE.VAL.MEMBRANE):
trait.append(constants.CT_TYPE.VAL.MEMBRANE)
except KeyError:
pass
return trait
[docs]class McproTyper(TyperBase):
NAME = "mcpro"
# Type codes:
UNKNOWN = 0
RAW_REGULAR = 201
RAW_FEP = 202
STRING = {
RAW_REGULAR: "a raw geometry for regular monte carlo simulations",
RAW_FEP: "raw geometries created for FEP calculations",
}
RULE = {
"raw_regular": [RAW_REGULAR,],
"raw_fep": [RAW_FEP,],
"raw": [
RAW_FEP,
RAW_REGULAR,
],
"regular": [RAW_REGULAR,],
"fep": [RAW_FEP,],
"auto": [
RAW_FEP,
RAW_REGULAR,
],
}
[docs] @staticmethod
def detect(struc, filename):
# McproTyper.RAW_FEP?
env_ct = []
ref_ct = []
mut_ct = []
for st in struc:
try:
if (st.property[constants.FEP_FRAGNAME] == "none"):
ref_ct.append(st)
else:
mut_ct.append(st)
except KeyError:
env_ct.append(st)
if (ref_ct != [] and mut_ct != []):
for st in ref_ct + mut_ct:
try:
a = st.atom[1].property[constants.FEP_SUBST]
except KeyError:
raise KeyError(
f"Property '{constants.FEP_SUBST}' not defined for perturbed atoms"
)
if (len(ref_ct) > 1):
raise TypeError(
"More than 1 reference CT were defined for perturbations")
return McproTyper.RAW_FEP
elif (ref_ct == [] and mut_ct != []):
raise TypeError("Reference CT not found")
return McproTyper.RAW_REGULAR
[docs] @staticmethod
def has_rule(rule):
return rule in McproTyper.RULE
[docs] @staticmethod
def check_rule(systype, rule):
return systype in McproTyper.RULE[rule]
[docs] @staticmethod
def check_system(struc, filename, rule):
return McproTyper.check_rule(McproTyper.detect(struc, filename), rule)
[docs]class WatermapTyper(TyperBase):
NAME = "watermap"
RAW_REGULAR = 201
STRING = {
RAW_REGULAR: "a raw geometry for regular watermap jobs",
}
RULE = {
"regular": [RAW_REGULAR,],
"": [RAW_REGULAR,],
}
[docs] @staticmethod
def detect(struc, filename):
return WatermapTyper.RAW_REGULAR
[docs] @staticmethod
def has_rule(rule):
return rule in WatermapTyper.RULE
[docs] @staticmethod
def check_rule(systype, rule):
return systype in WatermapTyper.RULE[rule]
[docs] @staticmethod
def check_system(struc, filename, rule):
return WatermapTyper.check_rule(WatermapTyper.detect(struc, filename),
rule)
[docs] @staticmethod
def prepare(stage, filename):
import schrodinger.utils.sea as sea
struc = cst.read_all_ct(filename)
solute_fname = filename + "_solute.mae"
ligand_fname = filename + "_ligand.mae"
recept_fname = filename + "_recept.mae"
with structure.StructureWriter(solute_fname) as writer:
for ct in struc:
if (ct.property[constants.CT_TYPE] not in [
constants.CT_TYPE.VAL.LIGAND,
constants.CT_TYPE.VAL.RECEPTOR,
]):
writer.append(ct)
with structure.StructureWriter(ligand_fname) as writer:
for ct in struc:
if (ct.property[constants.CT_TYPE] in [
constants.CT_TYPE.VAL.LIGAND,
]):
writer.append(ct)
with structure.StructureWriter(recept_fname) as writer:
for ct in struc:
if (ct.property[constants.CT_TYPE] in [
constants.CT_TYPE.VAL.RECEPTOR,
]):
writer.append(ct)
new_set_family = sea.Map()
new_set_family["vrun"] = sea.Map(
"jin_must_transfer_file = ['!append!' '%s' '%s'] "
"backend.vrun.plugin.TestParticleInsertion.ligand_file = '%s'" % (
ligand_fname,
"$JOBNAME-cluster.maegz",
os.path.basename(ligand_fname),
))
new_set_family["watermap_cluster"] = sea.Map(
"ligand_file = '%s' protein_file = '%s'" % (
ligand_fname,
recept_fname,
))
new_set_family["watermap_post_analysis"] = sea.Map(
"ligand_file = '%s' protein_file = '%s'" % (
ligand_fname,
recept_fname,
))
new_set_family["simulate"] = sea.Map(
"jin_must_transfer_file = ['!append!' '%s'] "
"backend.mdsim.plugin.SpatialActiveSite.ligand_file = '%s'" % (
ligand_fname,
os.path.basename(ligand_fname),
))
new_set_family["trim"] = sea.Map("erase2 = ['!append!' [0 '%s']]" %
ligand_fname)
stage.param.set_family.update(new_set_family)
[docs]class SysType:
[docs] def __init__(self, typer_rule=None):
try:
typer, rule = typer_rule.split(':')
except ValueError:
typer, rule = typer_rule, ""
typer = typer.strip()
rule = rule.strip()
try:
self.typer = TyperBase.typer_cls[typer]
except KeyError:
raise UnknownAppError("Unknown application: %s" % typer)
self.rule = rule
if (rule and not self.typer.has_rule(self.rule)):
raise UnknownRuleError("Unknown rule: %s, for application \"%s\"" %
(rule, typer))
[docs] def check_system(self, filename):
struc = cst.read_all_ct(filename)
try:
systype = self.typer.detect(struc, filename)
except Exception as e:
raise DetectionError("Detecting failed: %s" % str(e))
return (self.typer.check_rule(systype, self.rule), systype,
self.typer.STRING[systype])
[docs] def get_trait(self, filename):
struc = cst.read_all_ct(filename)
return self.typer.get_trait(struc)
[docs] def prepare(self, *arg, **kwarg):
self.typer.prepare(*arg, **kwarg)