Source code for pyCADD.Dynamic.template
# This file is a database of all the templates used in the Dynamic module.
# using str.format() to replace the placeholders with the actual values.
from abc import ABC, abstractmethod
from typing import Literal, Union
#########################
# Basic Class Definition
#########################
[docs]class BaseConstructor(ABC):
def __init__(self, state_dict: dict) -> None:
"""
Base class for all the constructors.
"""
self._state_dict = state_dict.copy()
self._state_keys = list(self._state_dict.keys())
for key in self._state_keys:
self.__dict__[key] = self._state_dict[key]
[docs] def get_state_dict(self) -> dict:
"""
Return the current state of the constructor as a dict.
Returns
-------
dict
Current state of the constructor.
"""
return {key: self.__dict__[key] for key in self._state_keys}
[docs] @abstractmethod
def to_string(self) -> str:
"""
Return the current state with formated template as a string.
Returns
-------
str
Formated template as a string.
"""
...
[docs] def to_dict(self) -> dict:
"""
Return the current state as a dict with current state.
Returns
-------
dict
Current state as a dict.
"""
return self.get_state_dict()
[docs] def save(self, file_path: str) -> None:
"""
Save the current state as a file.
"""
with open(file_path, "w") as f:
f.write(self.to_string())
[docs] def add_attr(self, **kwargs) -> None:
"""
Add attributes to the constructor.
"""
for key in kwargs.keys():
if key in self._state_keys:
raise KeyError(
f"Attribute {key} already exists in the current state.")
self.__dict__.update(kwargs)
self._state_keys.extend(list(kwargs.keys()))
[docs] def get_attr(self, attr_name: str) -> str:
"""
Get the value of the attribute.
"""
return self.get_state_dict()[attr_name]
[docs] def set_attr(self, **kwargs) -> None:
"""
Set the value of the attribute.
"""
for key in kwargs.keys:
if key not in self._state_keys:
raise KeyError(
f"Attribute {key} can not be found in the current state.")
self.__dict__.update(kwargs)
[docs] def del_attr(self, attr_name: str) -> None:
"""
Delete the attribute from the current state.
"""
if attr_name not in self._state_keys:
raise KeyError(
f"Attribute {attr_name} can not be found in the current state.")
del self.__dict__[attr_name]
self._state_keys.remove(attr_name)
_LEAP_BASE_TEMPLATE_APO = """
############Protein###########
pro = loadpdb {protein_file_path}
saveamberparm pro {file_prefix}_pro.prmtop {file_prefix}_pro.inpcrd
##############################
##########Complex#############
com = combine {pro_lig}
savepdb com {file_prefix}_com.pdb
saveamberparm com {file_prefix}_com.prmtop {file_prefix}_com.inpcrd
solvatebox com {box_type} {box_size}
check com
addions com {add_ions_type} 0
saveamberparm com {file_prefix}_comsolvate.prmtop {file_prefix}_comsolvate.inpcrd
##############################
quit
"""
_LEAP_BASE_TEMPLATE_LIGAND = """
############Ligand############
loadamberparams {frcmod_file_path}
lig = loadmol2 {ligand_file_path}
saveamberparm lig {file_prefix}_lig.prmtop {file_prefix}_lig.inpcrd
##############################
"""
_LEAP_BASE_TEMPLATE = _LEAP_BASE_TEMPLATE_LIGAND + _LEAP_BASE_TEMPLATE_APO
[docs]class LeapConstructor(BaseConstructor):
def __init__(self, state_dict: dict) -> None:
"""
LEaP 文件用于生成蛋白-配体复合物的拓扑和坐标文件。
LEaP file is used to generate the topology and coordinate files for the protein-ligand complex.
state_dict = {
"force_field": [...] ,
"frcmod_file_path": ... ,
"ligand_file_path": ... ,
"file_prefix": ... ,
"protein_file_path": ... ,
"box_type": ... ,
"box_size": ... ,
"add_ions_type": ...
}
"""
super().__init__(state_dict)
self.add_attr(pro_lig=r"{pro lig}")
@property
def base_template(self) -> str:
"""
Return the base template of the LEaP file.
"""
if self.get_state_dict().get("ligand_file_path") is None:
return _LEAP_BASE_TEMPLATE_APO
else:
return _LEAP_BASE_TEMPLATE
def _get_force_field_template(self) -> str:
"""
Return the force field template.
"""
_tmplate = "\n".join(
[f"source leaprc.{ff}" for ff in self.get_state_dict()["force_field"]])
return _tmplate
[docs] def to_string(self) -> str:
output_str = self._get_force_field_template()
output_str += self.base_template.format(**self.get_state_dict())
return output_str
[docs]class SimulationConstructor(BaseConstructor):
def __init__(self, state_dict: dict, type: Literal['cntrl', 'wt'], end_cfg: bool = True, title: str = None) -> None:
"""
MD 模拟输入文件用于执行 MD 模拟所需的能量最小化、升温加热、平衡及正式模拟。
MD Simulation input file is used to perform the energy minimization, heating, equilibration and production.
Parameters
----------
state_dict : dict
The state dict of the simulation stage.
type : Literal['cntrl', 'wt']
The type of the simulation stage.
end_cfg: bool, optional
Whether to add string "END" to the end of input file. Defaults to True.
title: str, optional
The title of the simulation stage. Defaults to None.
"""
super().__init__(state_dict)
self._type = type
self._end_cfg = end_cfg
self.title = title
[docs] def to_string(self) -> str:
if self.title is not None:
output_str = f"{self.title}\n&{self._type}\n"
else:
output_str = f"&{self._type}\n"
output_str += ",\n".join([f"{str(key)}={str(value)}" for key,
value in self.get_state_dict().items()])
output_str += "\n/\n"
if self._end_cfg:
output_str += '\nEND'
return output_str
[docs]class AnalysisConstructor(BaseConstructor):
def __init__(self, state_dict: dict, type: Literal['general', 'gb', 'pb', 'decomp', 'nmode'], title: str = None) -> None:
"""
MD 轨迹分析输入文件用于分析 MD 轨迹。
MD Trajectory Analysis input file is used to analyze the MD trajectory.
"""
super().__init__(state_dict)
self._type = type
self.title = title
[docs] def to_string(self) -> str:
output_str = f"# {self.title}\n" if self.title is not None else ""
output_str += f"&{self._type}\n"
output_str += ",\n".join([f" {str(key)}={str(value)}" for key,
value in self.get_state_dict().items()])
output_str += '\n/\n'
return output_str
[docs]class MultiConstructorManager:
def __init__(self, constructor_list: list = None) -> None:
self.constructor_list = constructor_list if constructor_list is not None else []
@property
def _state_dict(self) -> dict:
_state_dict = dict()
if len(self.constructor_list) == 0:
return _state_dict
else:
for constructor in self.constructor_list:
_state_dict.update(constructor.get_state_dict())
return _state_dict
[docs] def add_constructor(self, constructor: BaseConstructor) -> None:
self.constructor_list.append(constructor)
[docs] def del_constructor(self, constructor: BaseConstructor) -> None:
self.constructor_list.remove(constructor)
[docs] def to_string(self) -> str:
output_str = "".join([tmp.to_string()
for tmp in self.constructor_list])
return output_str
[docs] def save(self, file_path: str) -> None:
with open(file_path, 'w') as f:
f.write(self.to_string())
#########################
# Default Config
#########################
LEAP_DEFAULT = {
"force_field": [
"protein.ff14SB",
"gaff2",
"water.tip3p"
],
"ligand_file_path": None,
"frcmod_file_path": None,
"protein_file_path": None,
"file_prefix": None,
"box_size": "12.0",
"box_type": "TIP3PBOX",
"add_ions_type": "Na+"
}
MINIMIZE_DEFAULT = {
"imin": 1,
"cut": 8.0,
"ntpr": 10,
"ntb": 1,
"ntr": 0,
"ntc": 1,
"maxcyc": 10000,
"ncyc": 5000
}
NVT_DEFAULT = {
"imin": 0,
"ntx": 5,
"irest": 1,
"temp0": 300,
"ntt": 3,
"gamma_ln": 2.0,
"ntp": 0,
"ntb": 1,
"ntc": 2,
"ntf": 2,
"cut": 8.0,
"ntpr": 1000,
"ntwr": 1000,
"ntwx": 1000,
"nstlim": 500000,
"dt": 0.002
}
NPT_DEFAULT = {
"imin": 0,
"ntx": 5,
"irest": 1,
"temp0": 300,
"ntt": 3,
"gamma_ln": 2.0,
"ntp": 1,
"taup": 2.0,
"ntb": 2,
"ntc": 2,
"ntf": 2,
"cut": 8.0,
"ntpr": 1000,
"ntwr": 1000,
"ntwx": 1000,
"nstlim": 50000000,
"dt": 0.002
}
#########################
# LEaP Input File Class
#########################
[docs]class LeapInput(LeapConstructor):
def __init__(
self,
protein_file_path: str,
ligand_file_path: Union[str, None] = None,
frcmod_file_path: Union[str, None] = None,
file_prefix: Union[str, None] = None,
box_size: float = 12.0,
box_type: str = 'TIP3PBOX',
add_ions_type: str = 'Na+',
**kwargs) -> None:
'''
LEaP Input Config Constructor
Default Config:
"force_field": [
"protein.ff14SB",
"gaff2",
"water.tip3p"
],
"ligand_file_path": None,
"frcmod_file_path": None,
"protein_file_path": None,
"file_prefix": None,
"box_size": "12.0",
"box_type": "TIP3PBOX",
"add_ions_type": "Na+"
'''
file_prefix = "Untitled" if file_prefix is None else file_prefix
state_dict = LEAP_DEFAULT.copy()
state_dict.update(
{
"ligand_file_path": ligand_file_path,
"frcmod_file_path": frcmod_file_path,
"protein_file_path": protein_file_path,
"file_prefix": file_prefix,
"box_size": str(box_size),
"box_type": box_type,
"add_ions_type": add_ions_type
}
)
state_dict.update(kwargs)
super().__init__(state_dict)
#########################
# MD Simulation Input File
#########################
[docs]class MinimizeInput(SimulationConstructor):
def __init__(self, title: str = None, **kwargs) -> None:
"""
Minimization Input Config Constructor
Default Attributions:
{
"imin": 1,
"cut": 10.0,
"ntpr": 10,
"ntb": 1,
"ntr": 0,
"ntc": 1,
"maxcyc": 10000,
"ncyc": 5000,
}
"""
title = "Minimization" if title is None else title
state_dict = MINIMIZE_DEFAULT.copy()
state_dict.update(kwargs)
super().__init__(state_dict, "cntrl", title=title)
[docs]class RestrainedMinimizeInput(SimulationConstructor):
def __init__(self, restraintmask: str, restraint_wt=2.0, title: str = None, **kwargs) -> None:
"""
Restrained Minimization Input Config Constructor
Default Attributions:
{
"imin": 1,
"cut": 10.0,
"ntpr": 10,
"ntb": 1,
"ntr": 1,
"ntc": 1,
"maxcyc": 10000,
"ncyc": 5000,
"restraintmask": None,
"restraint_wt": 2.0
}
"""
title = 'Restrained Minimization' if title is None else title
state_dict = MINIMIZE_DEFAULT.copy()
state_dict.update({
"ntr": 1,
"restraintmask": restraintmask,
"restraint_wt": restraint_wt
})
state_dict.update(kwargs)
super().__init__(state_dict, "cntrl", title=title)
[docs]class NVTInput(SimulationConstructor):
def __init__(self, end_cfg: bool = True, title: str = None, **kwargs) -> None:
"""
NVT System Input Config Constructor.
Default Attributions:
{
"imin": 0,
"ntx": 5,
"irest": 1,
"temp0": 300,
"ntt": 3,
"gamma_ln": 2.0,
"ntp": 0,
"ntb": 1,
"ntc": 2,
"ntf": 2,
"cut": 10.0,
"ntpr": 1000,
"ntwr": 1000,
"ntwx": 1000,
"nstlim": 500000,
"dt": 0.002
}
Parameters
----------
end_cfg: bool, optional
Whether to add string "END" to the end of input file. Defaults to True.
title: str, optional
Title of the simulation. Defaults to None.
"""
title = "NVT Simulation" if title is None else title
state_dict = NVT_DEFAULT.copy()
state_dict.update(kwargs)
super().__init__(state_dict, "cntrl", end_cfg=end_cfg, title=title)
[docs]class HeatInput:
HEAT_TEMPLATE = {
"TYPE": "\'TEMP0\'",
"ISTEP1": 0,
"ISTEP2": 9000,
"VALUE1": 0.0,
"VALUE2": 300.0
}
def __init__(
self, tgt_temperature: float = 300.0, heat_step: int = 9000,
total_step: int = 10000, step_length: float = 0.002,
restraint_wt: float = None, restraintmask: str = None) -> None:
"""
System Heating Config Constructor.
Parameters
----------
tgt_temperature: float, optional
Target temperature of heating. Defaults to 300.0.
heat_step: int, optional
Heating system prior to this step, then, keep temperature until the end.
Should not larger than total steps. Defaults to 9000.
total_step: int, optional
Total steps of heating progress. Defaults to 10000.
step_length: float, optional
Length of each step. Defaults to 0.002.
restraint_wt: float, optional
Weight of the restraint. Defaults to None.
restraintmask: str, optional
Mask of the restraint. Defaults to None.
"""
self.stage_1_dict = self.HEAT_TEMPLATE.copy()
self.stage_1_dict.update({
"ISTEP2": heat_step,
"VALUE2": tgt_temperature
})
self.stage_2_dict = self.HEAT_TEMPLATE.copy()
self.stage_2_dict.update({
"ISTEP1": heat_step + 1,
"ISTEP2": total_step,
"VALUE1": tgt_temperature,
"VALUE2": tgt_temperature
})
self.tgt_temperature = tgt_temperature
self.heat_step = heat_step
self.total_step = total_step
self.step_length = step_length
self.restraint_wt = restraint_wt
self.restraintmask = restraintmask
self.manager = MultiConstructorManager()
@property
def _state_dict(self) -> dict:
return self.manager._state_dict
[docs] def add_nvt(self, **kwargs) -> None:
nvt_part = NVTInput(
end_cfg=False,
imin=0, irest=0, ntx=1,
nstlim=self.total_step, tempi=0.0,
temp0=self.tgt_temperature,
dt=self.step_length,
ntpr=100, ntwr=100, ntwx=100,
title="Heat",
**kwargs
)
self.manager.add_constructor(nvt_part)
[docs] def add_heat(self, stage_dict: dict = None) -> None:
if stage_dict is None:
self.manager.add_constructor(SimulationConstructor(
self.stage_1_dict, 'wt', end_cfg=False))
self.manager.add_constructor(SimulationConstructor(
self.stage_2_dict, 'wt', end_cfg=False))
self.manager.add_constructor(SimulationConstructor(
{'TYPE': "\'END\'"}, 'wt', end_cfg=True))
else:
self.manager.add_constructor(
SimulationConstructor(stage_dict, 'wt', end_cfg=False))
def _default_workflow(self) -> None:
if self.manager.is_empty():
if self.restraint_wt is not None and self.restraintmask is not None:
self.add_nvt(restraintmask=self.restraintmask,
restraint_wt=self.restraint_wt)
else:
self.add_nvt()
self.add_heat()
[docs] def save(self, file_path: str) -> None:
self._default_workflow()
self.manager.save(file_path)
[docs]class NPTInput(SimulationConstructor):
def __init__(self, end_cfg: bool = True, title: str = None, **kwargs) -> None:
"""
NPT System Input Config Constructor.
Default Attributions:
{
"imin": 0,
"ntx": 5,
"irest": 1,
"temp0": 300,
"ntt": 3,
"gamma_ln": 2.0,
"ntp": 1,
"taup": 2.0,
"ntb": 2,
"ntc": 2,
"ntf": 2,
"cut": 10.0,
"ntpr": 1000,
"ntwr": 1000,
"ntwx": 1000,
"nstlim": 50000000,
"dt": 0.002
}
"""
title = "NPT Simulation" if title is None else title
state_dict = NPT_DEFAULT.copy()
state_dict.update(kwargs)
super().__init__(state_dict, "cntrl", end_cfg=end_cfg, title=title)
#########################
# MD Trajectory Analysis Input File
#########################
MMGBSA_GENERAL_DEFAULT = {
"startframe": None,
"endframe": None,
"step_size": None,
"verbose": 2,
"keep_files": 1,
"netcdf": 1,
}
MMGBSA_GB_DEFAULT = {
"igb": 5,
"saltcon": 0.15
}
MMGBSA_DECOMP_DEFAULT = {
"idecomp": 1,
"dec_verbose": 1,
}
MMGBSA_PB_DEFAULT = {
"istrng": 0.15,
"fillration": 4.0,
}
MMGBSA_NMODE_DEFAULT = {
"maxcyc": 10000,
"drms": 0.001,
}
[docs]class MMGBSAInput:
def __init__(
self,
start_frame: int, end_frame: int,
step_size: int = 1
) -> None:
self.general_dict = MMGBSA_GENERAL_DEFAULT.copy()
self.general_dict.update(
startframe=start_frame, endframe=end_frame, step_size=step_size)
self.manager = MultiConstructorManager()
[docs] def add_general(self, title: str = None, **kwargs) -> None:
self.general_dict.update(kwargs)
general_part = AnalysisConstructor(
self.general_dict, 'general', title=title)
self.manager.add_constructor(general_part)
[docs] def add_gb(self, **kwargs) -> None:
gb_dict = MMGBSA_GB_DEFAULT.copy()
gb_dict.update(kwargs)
gb_part = AnalysisConstructor(gb_dict, 'gb')
self.manager.add_constructor(gb_part)
[docs] def add_pb(self, **kwargs) -> None:
pb_dict = MMGBSA_PB_DEFAULT.copy()
pb_dict.update(kwargs)
pb_part = AnalysisConstructor(pb_dict, 'pb')
self.manager.add_constructor(pb_part)
[docs] def add_decomp(self, **kwargs) -> None:
decomp_dict = MMGBSA_DECOMP_DEFAULT.copy()
decomp_dict.update(kwargs)
decomp_part = AnalysisConstructor(decomp_dict, 'decomp')
self.manager.add_constructor(decomp_part)
[docs] def add_nmode(self, **kwargs) -> None:
nmode_dict = MMGBSA_NMODE_DEFAULT.copy()
nmode_dict.update(kwargs)
nmode_part = AnalysisConstructor(nmode_dict, 'nmode')
self.manager.add_constructor(nmode_part)