import warnings
from pygromos.files.blocks._general_blocks import TITLE as generic_TITLE
from pygromos.files.blocks._general_blocks import _generic_gromos_block
from pygromos.utils.typing import List, Dict, Union, Number
# forward declarations
TITLE: generic_TITLE = generic_TITLE
class _generic_imd_block(_generic_gromos_block):
name = "genericBlock"
_order: List[List[str]] # contains the ordering of all fields in a block
def __init__(self, used: bool, content: Dict = None):
super().__init__(name=self.name, used=used, content=content)
def block_to_string(self) -> str:
result = ""
result += str(self.name) + self.line_seperator
for line in self._order:
for field_names in line:
result += "#" + self.field_seperator + self.field_seperator.join(field_names) + self.line_seperator
result += self.field_seperator
for element in field_names:
element = element.split("(")[0]
element = element.split(":")[0]
element = element.split(" ")[0]
element = element.replace(" ", "")
attribute = self.__getattribute__(element)
try:
if isinstance(attribute, (str, Number, bool)): # One element field
if isinstance(attribute, (bool, int)):
attribute = int(attribute)
elif isinstance(attribute, float): # supress scientific notation for floats!
attribute = format(attribute, "f")
result += str(attribute) + self.field_seperator
elif isinstance(attribute, List) and all(
[isinstance(x, (str, Number)) for x in attribute]
): # list content
if all([isinstance(x, str) for x in attribute]):
result += self.field_seperator.join(attribute) + self.field_seperator
elif all([isinstance(x, Number) for x in attribute]):
result += self.field_seperator.join(map(str, attribute)) + self.field_seperator
else:
raise ValueError(
"could not Interpret list: " + str(element) + "\t\n" + str(attribute) + "\nEOF\n"
)
elif isinstance(attribute, List) and all([isinstance(x, List) for x in attribute]): # matrices
pre_parsed_rows = map(lambda x: self.field_seperator.join(map(str, x)), attribute)
result += (self.line_seperator + self.field_seperator).join(
pre_parsed_rows
) + self.field_seperator
else:
result += (self.line_seperator + self.field_seperator).join(
map(self.field_seperator.join, attribute)
) + self.field_seperator
except ValueError:
raise ValueError(
"Could not convert attribute "
+ str(element)
+ " to string!\n value of attribute was: \n"
+ str(attribute)
+ "\nEOF\n"
)
result += self.line_seperator
result += "END\n"
return result
def read_content_from_str(self, content: List[str]):
keyLineNumb = 0
contentlines = []
for line in content:
if line.startswith("# "):
# a new key line was found add values to class
if len(contentlines) > 0:
self._parse_key_content(keyLineNumb=keyLineNumb, contentlines=contentlines)
keyLineNumb += 1 # go next key line
contentlines = [] # reset content storage
else:
# no key found -> add data to list to be later added to key
fields = line.split(self.field_seperator)
while "" in fields:
fields.remove("")
contentlines.append(fields)
# add fianl key after loop
self._parse_key_content(keyLineNumb=keyLineNumb, contentlines=contentlines)
def _parse_key_content(self, keyLineNumb: int = 0, contentlines: List[str] = []):
# print("----------------")
# print(contentlines)
# print(self._order[0])
# print(keyLineNumb)
if len(contentlines) == 1: # it's a one liner
if len(contentlines[0]) == len(self._order[0][keyLineNumb]):
# found key-value match
for key, field in zip(self._order[0][keyLineNumb], contentlines[0]):
# first bring key to attribute form
key = self._clean_key_from_order(key=key)
field = self._try_to_convert_field(field=field, key=key)
setattr(self, key, field)
else: # parse multi line
if len(self._order[0][keyLineNumb]) == 1:
# one key but multiple fields
key = self._clean_key_from_order(key=self._order[0][keyLineNumb][0])
field = self._try_to_convert_field(field=contentlines, key=key)
setattr(self, key, field)
else:
# set attribute with a list over multiple lines for keys in paralles (example temp baths)
for i, key in enumerate(self._order[0][keyLineNumb]):
field = [x[i] for x in contentlines]
key = self._clean_key_from_order(key=key)
field = self._try_to_convert_field(field=field, key=key)
setattr(self, key, field)
def _clean_key_from_order(self, key: str) -> str:
# key = key.replace("("," ").replace("."," ")
# key.split(" ")
# return key[0]
if "(" in key:
return key.split("(")[0]
if "." in key:
return key.split(".")[0]
if " " in key:
return key.split(" ")[0]
return key
def _try_to_convert_field(self, field: str, key: str) -> any:
if isinstance(field, str):
try:
if self.__annotations__[key] is bool:
return bool(int(field)) # solves the type problem for simple types
return self.__annotations__[key](field) # solves the type problem for simple types
except Exception:
return field
elif isinstance(field, list):
if all([isinstance(x, str) for x in field]):
try:
return [float(x) for x in field]
except Exception:
return field
elif all([isinstance(x, list) for x in field]) and all([[isinstance(x, list) for x in y] for y in field]):
try:
return [[float(x) for x in y] for y in field]
except Exception:
return field
[docs]class SYSTEM(_generic_imd_block):
"""System Block
The system block defines the number of solute molecules and solvent molecules
Attributes
----------
NPM: int
Number of Solute Molecules
NSM: int
Number of Solvent Molecules
"""
name: str = "SYSTEM"
# fields
NPM: int # number of Solute Molecules
NSM: int # number of Solvent Molecules
_order = [[["NPM", "NSM"]]]
def __init__(self, NPM: int = 0, NSM: int = 0, content: List[str] = None):
super().__init__(used=True, content=content)
if content is None:
self.NPM = int(NPM)
self.NSM = int(NSM)
[docs]class STEP(_generic_imd_block):
"""Step Block
This Block gives the number of simulation steps,
Attributes
-----------
NSTLIM: int
number of simulations Step till terminating.
T: float
Starting Time
DT: float
time step [fs]
"""
name: str = "STEP"
# fields
NSTLIM: int
T: float
DT: float
_order = [[["NSTLIM", "T", "DT"]]]
def __init__(self, NSTLIM: int = 0, T: float = 0, DT: float = 0, content: List[str] = None):
super().__init__(used=True, content=content)
if content is None:
self.NSTLIM = int(NSTLIM)
self.T = float(T)
self.DT = float(DT)
[docs]class REPLICA(_generic_imd_block):
"""REPLICA Block
This block is controlling the REPLICA settings in gromos. It works both for T-REMD and H-REMD
Attributes
----------
RETL: bool
Do you want to rund an RE simulation or not?
RETL=0 : turn off RE
RETL=1 : turn on RE
NRET: int
Number of temperatures
RET: List[float]
List of temperature at each replica
LRESCALE: bool
controls temperature scaling
0 don't scale temperatures after exchange trial
1 scale temperatures after exchange trial
NRELAM: int
number of lambda values
RELAM: List[float]
lambda value for each lambda-replica
RETS: List[float]
timestep of each LAMBDA replica
NRETRIAL: int
How many replica exchanges trials should be executed? (NRETRIAL*STEP.NSTLIM == total simulation time)
NREQUIL: int
How many equilibration runs shall be exectured? (NREQUIL*STEP.NSTLIM == total simulation time)
CONT: bool
Is this a continuation run?
0 start from one configuration file
1 start from multiple configuration files
Example Block
-------------
# RETL
1
# NRET
10
# RET(1..NRET)
300.0 320.0 340.0 360.0 380.0
400.0 420.0 440.0 460.0 480.0
# LRESCALE
1
# NRELAM
10
# RELAM(1..NRELAM
0.0 0.1 0.2 0.3 0.4
0.5 0.6 0.7 0.8 0.9
# RETS(1..NRELAM)
0.002 0.001 0.001 0.001 0.002
0.003 0.002 0.001 0.001 0.002
# NRETRIAL
100
# NREQUIL
10
# CONT
0
END
"""
name: str = "REPLICA"
RETL: bool
NRET: int
RET: List[float]
LRESCALE: bool
NRELAM: int
RELAM: List[float]
RETS: List[float]
NRETRIAL: int
NREQUIL: int
CONT: bool
_order = [
[
["RETL"],
["NRET"],
["RET(1 ... NRET)"],
["LRESCALE"],
["NRELAM"],
["RELAM(1 ... NRELAM)"],
["RETS(1 ... NRELAM)"],
["NRETRIAL", "NREQUIL", "CONT"],
]
]
def __init__(
self,
RETL: bool = False,
NRET: int = 0,
RET: List[float] = [],
LRESCALE: int = 0,
NRELAM: int = 0,
RELAM: List[float] = [],
RETS: List[float] = [],
NRETRIAL: int = 0,
NREQUIL: int = 0,
CONT: bool = False,
content: List[str] = None,
):
super().__init__(used=True, content=content)
if content is None:
self.RETL = bool(RETL)
self.NRET = int(NRET)
self.RET = RET
self.LRESCALE = bool(LRESCALE)
self.NRELAM = int(NRELAM)
self.RELAM = RELAM
self.RETS = RETS
self.NRETRIAL = int(NRETRIAL)
self.NREQUIL = int(NREQUIL)
self.CONT = bool(CONT)
[docs] def read_content_from_str(self, content: List[str]):
try:
setattr(self, "RETL", int(content[1].split()[0]))
setattr(self, "NRET", int(content[3].split()[0]))
T_values = list(map(float, content[5].split()))
if len(T_values) == self.NRET:
setattr(self, "RET", T_values)
else:
raise IOError("REPLICA: NRET was not equal to the number of Temperatures (RET) in IMD!")
setattr(self, "LRESCALE", int(content[7].split()[0]))
setattr(self, "NRELAM", int(content[9].split()[0]))
lambda_val = list(map(float, content[11].split()))
if len(lambda_val) == self.NRELAM:
setattr(self, "RELAM", lambda_val)
else:
raise IOError("REPLICA: NRELAM was not equal to the number of lambda values (RELAM) in IMD!")
lambda_timestep = list(map(float, content[13].split()))
if len(lambda_timestep) == self.NRELAM:
setattr(self, "RETS", lambda_timestep)
else:
raise IOError("REPLICA: NRELAM was not equal to the number of lambda timestep values (RETS) in IMD!")
[setattr(self, key, int(value)) for key, value in zip(self._order[0][-1], content[-1].split())]
except Exception as err:
raise IOError("Could not parse block from str - " + __class__.__name__ + "\n" + str(err.args))
[docs]class NEW_REPLICA_EDS(_generic_imd_block):
"""REPLICA_EDS Block
This block is controlling the REPLICA_EDS settings in gromos and is basically a mixture of EDS and RE block. (Don't use them when using this block!)
Attributes
----------
REEDS: bool
Shall REEDS be activated?
NRES: int
Number of s-Values
NEOFF: int
number of replica exchange eds energy Offset vectors (only used for 2D REEDS - still needs to be present ;))
NUMSTATES: int
Number of EDS-states
RES: List[float]
s_values for all replicas
EIR: List[List[float]]
energy offsets for all replicas and all states List[List[float]] = REPLICA[EDS_STATE[EIR]]
NERTRIAL: int
How many replica exchanges trials should be executed? (NRETRIAL*STEP.NSTLIM == total simulation time)
NREQUIL: int
How many equilibration runs shall be exectured? (NREQUIL*STEP.NSTLIM == total simulation time)
EDS_STAT_OUT: int
Shall the replica exchange information be outputted? (__future__ frequency of output.)
CONT: bool
Is this a continuation run?
"""
name: str = "REPLICA_EDS"
REEDS: bool
NRES: int
NEOFF: int
NUMSTATES: int
RES: List[float]
EIR: List[float]
NRETRIAL: int
NREQUIL: int
EDS_STAT_OUT: int
CONT: bool
PERIODIC: int
_order = [
[
["REEDS"],
["NRES", "NUMSTATES", "NEOFF"],
["RES(1 ... NRES)"],
["EIR(NUMSTATES x NRES)"],
["NRETRIAL", "NREQUIL", "CONT", "EDS_STAT_OUT", "PERIODIC"],
]
]
def __init__(
self,
REEDS: bool = False,
NRES: int = 0,
NUMSTATES: int = 0,
NEOFF: int = 0,
RES: List[float] = [],
EIR: List[List[float]] = [[]],
NRETRIAL: int = 0,
NREQUIL: int = 0,
EDS_STAT_OUT: int = 0,
CONT: bool = False,
PERIODIC: int = 0,
content: List[str] = None,
):
super().__init__(used=True, content=content)
if content is None:
self.REEDS = bool(REEDS)
self.NRES = int(NRES)
self.NEOFF = int(NEOFF)
self.NUMSTATES = int(NUMSTATES)
self.RES = RES
self.EIR = EIR
self.NRETRIAL = int(NRETRIAL)
self.NREQUIL = int(NREQUIL)
self.CONT = bool(CONT)
self.EDS_STAT_OUT = int(EDS_STAT_OUT)
self.PERIODIC = int(PERIODIC)
[docs] def read_content_from_str(self, content: List[str]):
try:
setattr(self, "REEDS", int(content[1].split()[0]))
setattr(self, "NRES", int(content[3].split()[0]))
setattr(self, "NEOFF", int(content[3].split()[1]))
setattr(self, "NUMSTATES", int(content[3].split()[2]))
s_values = list(map(float, content[5].split()))
if len(s_values) == self.NRES:
setattr(self, "RES", s_values)
else:
raise IOError("REPLICA_EDS: NRES was not equal to the number of s-values in IMD!")
EIR = []
for ind in range(7, 7 + self.NUMSTATES):
EIR_line = list(map(float, content[ind].split()))
if len(EIR_line) != self.NRES:
raise IOError("REPLICA_EDS: NRES was not equal to the number of EIRs given in IMD!")
EIR.append(EIR_line)
setattr(self, "EIR", EIR)
[setattr(self, key, int(value)) for key, value in zip(self._order[0][-1], content[-1].split())]
except Exception as err:
raise IOError("Could not parse block from str - " + __class__.__name__ + "\n" + str(err.args))
[docs]class REPLICA_EDS(_generic_imd_block):
name: str = "REPLICA_EDS"
REEDS: bool
NRES: int
NUMSTATES: int
RES: List[float]
EIR: List[float]
NRETRIAL: int
NREQUIL: int
EDS_STAT_OUT: int
CONT: bool
_order = [
[
["REEDS"],
["NRES", "NUMSTATES"],
["RES(1 ... NRES)"],
["EIR(NUMSTATES x NRES)"],
["NRETRIAL", "NREQUIL", "CONT", "EDS_STAT_OUT"],
]
]
[docs] def __init__(
self,
REEDS: bool = True,
NRES: int = 0,
NUMSTATES: int = 0,
RES: List[float] = [0],
EIR: List[List[float]] = [[0]],
NRETRIAL: int = 0,
NREQUIL: int = 0,
EDS_STAT_OUT: int = 0,
CONT: bool = True,
content: List[str] = None,
):
"""REPLICA_EDS Block
This block is controlling the REPLICA_EDS settings in gromos and is basically a mixture of EDS and RE block. (Don't use them when using this block!)
Attributes
----------
REEDS: bool
Shall REEDS be activated?
NRES: int
Number of s-Values
NUMSTATES: int
Number of EDS-states
RES: List[float]
s_values for all replicas
EIR: List[List[float]]
energy offsets for all replicas and all states List[List[float]] = REPLICA[EDS_STATE[EIR]]
NERTRIAL: int
How many replica exchanges trials should be executed? (NRETRIAL*STEP.NSTLIM == total simulation time)
NREQUIL: int
How many equilibration runs shall be exectured? (NREQUIL*STEP.NSTLIM == total simulation time)
EDS_STAT_OUT: int
Shall the replica exchange information be outputted? (__future__ frequency of output.)
CONT: bool
Is this a continuation run?
"""
super().__init__(used=True, content=content)
if content is None:
self.REEDS = REEDS
self.NRES = NRES
self.NUMSTATES = NUMSTATES
self.RES = RES
self.EIR = EIR
self.NRETRIAL = NRETRIAL
self.NREQUIL = NREQUIL
self.CONT = CONT
self.EDS_STAT_OUT = EDS_STAT_OUT
[docs] def read_content_from_str(self, content: List[str]):
try:
setattr(self, "REEDS", int(content[1].split()[0]))
setattr(self, "NRES", int(content[3].split()[0]))
setattr(self, "NUMSTATES", int(content[3].split()[1]))
s_values = list(map(float, content[5].split()))
if len(s_values) == self.NRES:
setattr(self, "RES", s_values)
else:
raise IOError("REPLICA_EDS: NRES was not equal to the number of s-values in IMD!")
EIR = []
for ind in range(7, 7 + self.NUMSTATES):
EIR_line = list(map(float, content[ind].split()))
if len(EIR_line) != self.NRES:
raise IOError("REPLICA_EDS: NRES was not equal to the number of EIRs given in IMD!")
EIR.append(EIR_line)
setattr(self, "EIR", EIR)
[setattr(self, key, int(value)) for key, value in zip(self._order[0][-1], content[-1].split())]
except Exception as err:
raise IOError("Could not parse block from str - " + __class__.__name__ + "\n" + str(err.args))
[docs]class BOUNDCOND(_generic_imd_block):
"""Boundary Condition Block
This block describes the boundary condition of the coordinate system.
Attributes
----------
NTB : int, optional
Boundary conditions, by default 0
-1 : truncated octahedral
0 : vacuum
1 : rectangular
2 : triclinic
NDFMIN : int, optional
number of degrees of freedom subtracted for temperature, by default 0
"""
name: str = "BOUNDCOND"
NTB: int
NDFMIN: int
_order = [[["NTB", "NDFMIN"]]]
def __init__(self, NTB: int = 0, NDFMIN: int = 0, content: List[str] = None):
super().__init__(used=True, content=content)
if content is None:
self.NTB = int(NTB)
self.NDFMIN = int(NDFMIN)
[docs]class STOCHDYN(_generic_imd_block):
"""Stochastic Dynamics block
This block is for Stochastic Dynamics
Attributes
----------
NTSD: int
NTFR: int
NSFR: int
NBREF: int
RCUTF: float
CFRIC: float
TEMPSD: float
"""
name: str = "STOCHDYN"
NTSD: int
NTFR: int
NSFR: int
NBREF: int
RCUTF: float
CFRIC: float
TEMPSD: float
_order = [[["NTSD", "NTFR", "NSFR", "NBREF", "RCUTF", "CFRIC", "TEMPSD"]]]
def __init__(
self,
NTSD: int = 0,
NTFR: int = 0,
NSFR: int = 0,
NBREF: int = 0,
RCUTF: float = 0,
CFRIC: float = 0,
TEMPSD: float = 0,
content: List[str] = None,
):
super().__init__(used=True, content=content)
if content is None:
self.NTSD = int(NTSD)
self.NTFR = int(NTFR)
self.NSFR = int(NSFR)
self.NBREF = int(NBREF)
self.RCUTF = float(RCUTF)
self.CFRIC = float(CFRIC)
self.TEMPSD = float(TEMPSD)
[docs]class PERTURBATION(_generic_imd_block):
"""Pertubation Block
This block is for Thermodynamic integration
Attributes
----------
NTG: int
0..1 controls use of free-energy calculation.
0: no free-energy calculation (default)
1: calculate dH/dRLAM
NRDGL: int
0,1 controls reading of initial value for RLAM.
0: use initial RLAM parameter from PERTURBATION block
1: read from configuration
RLAM: float
0.0..1.0 initial value for lambda
DLAMT: float
>= 0.0 rate of lambda increase in time.
ALPHLJ: float
>= 0.0 Lennard-Jones soft-core parameter
ALPHC: float
>= 0.0 Coulomb-RF soft-core parameter
NLAM: int
> 0 power dependence of lambda coupling
NSCALE: int
0..2 controls use of interaction scaling
0: no interaction scaling
1: interaction scaling
2: perturbation for all atom pairs with scaled
interactions. No perturbation for others.
ExampleBlock
____________
# NTG NRDGL RLAM DLAMT
1 0 0.0 0.0
# ALPHLJ ALPHC NLAM NSCALE
0.5 0.5 2 0
"""
name: str = "PERTURBATION"
NTG: int
NRDGL: int
RLAM: float
DLAMT: float
ALPHLJ: float
ALPHC: float
NLAM: int
NSCALE: int
_order = [[["NTG", "NRDGL", "RLAM", "DLAMT"], ["ALPHLJ", "ALPHC", "NLAM", "NSCALE"]]]
def __init__(
self,
NTG: int = 0,
NRDGL: int = 0,
RLAM: float = 0,
DLAMT: float = 0,
ALPHLJ: float = 0,
ALPHC: float = 0,
NLAM: int = 0,
NSCALE: int = 0,
content: List[str] = None,
):
super().__init__(used=True, content=content)
if content is None:
self.NTG = int(NTG)
self.NRDGL = int(NRDGL)
self.RLAM = float(RLAM)
self.DLAMT = float(DLAMT)
self.ALPHLJ = float(ALPHLJ)
self.ALPHC = float(ALPHC)
self.NLAM = int(NLAM)
self.NSCALE = int(NSCALE)
[docs]class PRECALCLAM(_generic_imd_block):
"""
Attributes
----------
NTG: int
Returns
-------
"""
name = "PRECALCLAM"
NRLAM: int
MINLAM: float
MAXLAM: float
_order = [[["NRLAM", "MINLAM", "MAXLAM"]]]
[docs] def __init__(self, NRLAM: int = 0, MINLAM: float = 0, MAXLAM: float = 0, content: List[str] = None):
"""
Can be used to caluclate multiple extra lambda values
Parameters
----------
NRLAM: int
0 : off
>1 : precalculating energies for NRLAM extra lambda values
MINLAM
between 0 and 1: minimum lambda value to precalculate energies
MAXLAM
between MINLAM and 1: maximum lambda value to precalculate energies
"""
super().__init__(used=True, content=content)
if content is None:
self.NRLAM = int(NRLAM)
self.MINLAM = float(MINLAM)
self.MAXLAM = float(MAXLAM)
[docs]class MULTIBATH(_generic_imd_block):
"""MULTIBATH Block
This block controls the Thermostat of a simulation. Multiple temperature baths can be coupled.
Attributes
----------
ALGORITHM: int
temperature coupling algorithm
weak-coupling(0)
nose-hoover(1)
nose-hoover-chains(2) num
(where num is the number of chains to use)
NUM: int, optional
Mumber of chains in Nosé Hoover chains scheme [only specify when needed]
NBATHS: int
Number of temperature baths
TEMP0: List[float]
temperature of each bath (list len == NBATHS)
TAU: List[float]
coupling of the temperaturebath to the system (list len == NBATHS)
DOFSET: int
Number of set of Degrees of freedom.
LAST: List[int] (list len == DOFSET)
Last atoms of each DOFSet
COMBATH: List[int] (list len == DOFSET)
Index of the temperature baths
IRBATH: List[int] (list len == DOFSET)
IRBAHT?
See Also
--------
_generic_imd_block, _generic_gromos_block
"""
name: str = "MULTIBATH"
ALGORITHM: int
NUM: int
NBATHS: int
TEMP0: List[float]
TAU: List[float]
DOFSET: int
LAST: List[int]
COMBATH: List[int]
IRBATH: List[int]
_order: List[List[str]] = [
[
["ALGORITHM"],
["NUM"],
["NBATHS"],
["TEMP0(1 ... NBATHS)", "TAU(1 ... NBATHS)"],
["DOFSET"],
["LAST(1 ... DOFSET)", "COMBATH(1 ... DOFSET)", "IRBATH(1 ... DOFSET)"],
]
]
# num is not part of the imd file!?
def __init__(
self,
ALGORITHM: int = 0,
NBATHS: int = 0,
TEMP0: List[float] = [],
TAU: List[float] = [],
DOFSET: int = 0,
LAST: List[int] = [],
COMBATH: List[int] = [],
IRBATH: List[int] = [],
NUM: int = None,
content: List[str] = None,
):
if content is None:
self.ALGORITHM = int(ALGORITHM)
self.NUM = NUM
self.NBATHS = int(NBATHS)
self.TEMP0 = list(TEMP0)
self.TAU = list(TAU)
self.DOFSET = int(DOFSET)
self.LAST = list(LAST)
self.COMBATH = list(COMBATH)
self.IRBATH = list(IRBATH)
if not len(TEMP0) == len(TAU):
warnings.warn("Warning in MULTIBATH block. There must be the same number of TEMP0 and TAU parameters")
if not len(TEMP0) == int(NBATHS):
warnings.warn("Warning in MULTIBATH block. There must be the same number of BATHS and TEMP0 parameters")
warnings.warn(TEMP0)
warnings.warn(NBATHS)
if not len(LAST) == int(NBATHS):
warnings.warn("Warning in MULTIBATH block. There must be the same number of BATHS and LAST parameters")
if not len(LAST) == len(COMBATH):
warnings.warn(
"Warning in MULTIBATH block. There must be the same number of COMBATH and LAST parameters."
)
if not len(LAST) == len(IRBATH):
warnings.warn("Warning in MULTIBATH block. There must be the same number of IRBATH and LAST parameters")
super().__init__(used=True)
else:
super().__init__(used=True, content=content)
[docs] def read_content_from_str(self, content: List[str]):
try:
setattr(self, self._order[0][0][0], int(content[1].split()[0]))
if "NUM" in content[0]:
setattr(self, self._order[0][1][0], int(content[1].split()[1]))
setattr(self, self._order[0][2][0], int(content[3].split()[0]))
TEMP0 = []
TAU = []
# Read in the temperatures of each temp bath
for ind in range(5, 5 + self.NBATHS):
fields = list(map(float, content[ind].split()))
TEMP0.append(fields[0])
TAU.append(fields[1])
setattr(self, "TEMP0", TEMP0)
setattr(self, "TAU", TAU)
# Read in DOFSET
setattr(self, self._order[0][4][0], int(content[5 + self.NBATHS + 1].split()[0]))
LAST = []
COMBATH = []
IRBATH = []
# Read in the last atoms of each temp bath
for ind in range(5 + 3 + self.NBATHS, 5 + 3 + 2 * self.NBATHS):
fields = list(map(float, content[ind].split()))
LAST.append(fields[0])
COMBATH.append(fields[1])
IRBATH.append(fields[2])
setattr(self, "LAST", LAST)
setattr(self, "COMBATH", COMBATH)
setattr(self, "IRBATH", IRBATH)
except Exception as err:
raise IOError("Could not parse " + __class__.__name__ + " block!\n" + str(err.args))
[docs] def adapt_multibath(
self,
last_atoms_bath: Dict[int, int],
algorithm: int = None,
num: int = None,
T: Union[float, List[float]] = None,
TAU: float = None,
) -> None:
"""adapt_multibath
This function is adding each atom set into a single multibath.
#TODO implementation not correct with com_bath and irbath! Works for super simple cases though
Parameters
----------
last_atoms_bath : Dict[int,int]
lastatom:bath
algorithm : int
int code for algorihtm
T : float,List[float], optional
temperature value
TAU : float, optional
coupling var
Returns
-------
None
"""
if T is None:
if any([self.TEMP0[0] != T_test for T_test in self.TEMP0]):
raise ValueError("Temperatures are not the same, this is not implemented in adapt multibath!")
T = self.TEMP0[0]
else:
self.TEMP0 = [float(T) for x in range(len(self.TEMP0))]
if TAU is None:
if any([self.TAU[0] != T_test for T_test in self.TAU]):
raise ValueError("Temperatures are not the same, this is not implemented in adapt multibath!")
TAU = self.TAU[0]
else:
self.TAU = [float(TAU) for x in range(len(self.TAU))]
if algorithm is None:
pass
else:
self.ALGORITHM = int(algorithm)
if num is not None:
self.NUM = int(num)
# print("MBATH")
# print(last_atoms_bath)
# print("\n")
# print(last_atoms_bath.values())
# print("\n")
# print(set(last_atoms_bath.values()))
self.NBATHS = len(set(last_atoms_bath.values()))
self.DOFSET = len(last_atoms_bath)
self.LAST = [int(last_atom) for last_atom in last_atoms_bath]
self.COMBATH = [int(last_atoms_bath[last_atom]) for last_atom in last_atoms_bath]
self.IRBATH = [int(last_atoms_bath[last_atom]) for last_atom in last_atoms_bath]
if self.NBATHS != len(self.TEMP0):
self.TEMP0 = [float(self.TEMP0[0]) for x in range(self.NBATHS)]
self.TAU = [float(self.TAU[0]) for x in range(self.NBATHS)]
[docs] def block_to_string(self) -> str:
result = ""
result += str(self.name) + "\n"
result += "# " + self.field_seperator.join(self._order[0][0]) + "\n"
result += " " + str(int(self.ALGORITHM)) + "\n"
if self.ALGORITHM == "2":
if self.NUM is None:
raise Exception("You need to specify the NUM parameter for MULTIBATH if ALGORITHM is 2!")
result += "# " + self.field_seperator.join(self._order[0][1]) + "\n"
result += " " + str(self.NUM) + "\n"
result += "# " + self.field_seperator.join(self._order[0][2]) + "\n"
result += " " + str(int(self.NBATHS)) + "\n"
result += "# " + self.field_seperator.join(self._order[0][3]) + "\n"
for index in range(len(self.TEMP0)):
result += " " + str(float(self.TEMP0[index])) + self.field_seperator + str(float(self.TAU[index])) + "\n"
result += "# " + self.field_seperator.join(map(str, self._order[0][4])) + "\n"
result += " " + str(int(self.DOFSET)) + "\n"
result += "# " + self.field_seperator.join(map(str, self._order[0][5])) + "\n"
for index in range(len(self.LAST)):
result += (
" "
+ str(int(self.LAST[index]))
+ self.field_seperator
+ str(int(self.COMBATH[index]))
+ self.field_seperator
+ str(int(self.IRBATH[index]))
+ "\n"
)
result += "END\n"
return result
[docs]class PRESSURESCALE(_generic_imd_block):
"""PRESSURESCALE Block
This block controls the barostat of the simulation
Attributes
-----------
COUPLE: int
off(0), calc(1), scale(2)
SCALE: int
off(0), iso(1), aniso(2), full(3), semianiso(4)
COMP: float
compessibility
TAUP: float
coupling strength
VIRIAL: int
none(0), atomic(1), group(2)
SEMIANISOTROPIC: List[int]
(semianisotropic couplings: X, Y, Z)
e.g. 1 1 2: x and y jointly coupled and z separately coupled
e.g. 0 0 1: constant area (xy-plane) and z coupled to a bath
PRES0: List[List[float]]
reference pressure
"""
name: str = "PRESSURESCALE"
COUPLE: int
SCALE: int
COMP: float
TAUP: float
VIRIAL: int
SEMIANISOTROPIC: List[int]
PRES0: List[List[float]]
_order = [
[["COUPLE", "SCALE", "COMP", "TAUP", "VIRIAL"], ["SEMIANISOTROPIC COUPLINGS(X, Y, Z)"], ["PRES0(1...3,1...3)"]]
]
def __init__(
self,
COUPLE: int = 0,
SCALE: int = 0,
COMP: float = 0,
TAUP: float = 0,
VIRIAL: int = 0,
SEMIANISOTROPIC: List[int] = [],
PRES0: List[List[float]] = [[0, 0, 0], [0, 0, 0], [0, 0, 0]],
content: List[str] = None,
):
super().__init__(used=True, content=content)
if content is None:
self.COUPLE = int(COUPLE)
self.SCALE = int(SCALE)
self.COMP = float(COMP)
self.TAUP = float(TAUP)
self.VIRIAL = int(VIRIAL)
self.SEMIANISOTROPIC = list(SEMIANISOTROPIC)
self.PRES0 = list(PRES0)
[docs] def read_content_from_str(self, content: List[str]):
try:
[setattr(self, key, value) for key, value in zip(self._order[0][0], content[1].split())]
setattr(self, "SEMIANISOTROPIC", list(map(int, content[3].split())))
setattr(self, "PRES0", [list(map(float, x.split())) for x in content[5:]])
except Exception as err:
raise IOError("Could not read string for block " + __class__.__name__ + "\n" + str(err.args))
[docs]class FORCE(_generic_imd_block):
"""FORCE Block
this Block is controlling the forcefield options. It can turn on and of terms, as well as generate force sub groups for analysis.
Attributes
-----------
BONDS: bool
ANGLES: bool
IMPROPER: bool
DIHEDRAL: bool
ELECTROSTATIC: bool
VDW: bool
NEGR: int
Number of Energy subgroups
NRE: List[int]
List of last atoms for Energy subgroups. (NRE len == NEGR)
"""
name: str = "FORCE"
BONDS: bool
ANGLES: bool
IMPROPER: bool
DIHEDRAL: bool
ELECTROSTATIC: bool
VDW: bool
NEGR: int
NRE: List[int]
_order = [[["BONDS", "ANGLES", "IMPROPER", "DIHEDRAL", "ELECTROSTATIC", "VDW"], ["NEGR", "NRE"]]]
[docs] def __init__(
self,
BONDS: bool = True,
ANGLES: bool = True,
IMPROPER: bool = True,
DIHEDRAL: bool = True,
ELECTROSTATIC: bool = True,
VDW: bool = True,
NEGR: int = 0,
NRE: List[int] = [],
content: List[str] = None,
):
"""
Args:
BONDS:
ANGLES:
IMPROPER:
DIHEDRAL:
ELECTROSTATIC:
VDW:
NEGR:
NRE (list):
"""
if content is None:
self.BONDS = bool(int(BONDS))
self.ANGLES = bool(int(ANGLES))
self.IMPROPER = bool(int(IMPROPER))
self.DIHEDRAL = bool(int(DIHEDRAL))
self.ELECTROSTATIC = bool(int(ELECTROSTATIC))
self.VDW = bool(int(VDW))
try:
self.NEGR = int(NEGR)
except Exception:
if isinstance(NEGR, list):
self.NEGR = int(NEGR[0])
NRE = NEGR[1:] + NRE
self.NRE = NRE
super().__init__(used=True)
else:
super().__init__(used=True, content=content)
def _parse_key_content(self, keyLineNumb: int = 0, contentlines: List[str] = []):
if keyLineNumb == 0:
for key, field in zip(self._order[0][keyLineNumb], contentlines[0]):
# first bring key to attribute form
key = self._clean_key_from_order(key=key)
field = self._try_to_convert_field(field=field, key=key)
setattr(self, key, field)
else:
# NEGR
key = "NEGR"
field = self._try_to_convert_field(field=contentlines[0][0], key=key)
setattr(self, key, field)
# NRE
key = "NRE"
field = self._try_to_convert_field(field=contentlines[0][1:], key=key)
setattr(self, key, field)
[docs] def read_content_from_str(self, content: List[str]):
try:
fields = content[1].split(self.field_seperator)
while "" in fields:
fields.remove("")
self._parse_key_content(keyLineNumb=0, contentlines=[fields])
setattr(self, self._order[0][1][0], int(content[3].split()[0]))
setattr(self, self._order[0][1][1], list(map(int, content[3].split()[1:])))
except Exception as err:
raise IOError("Could not parse FORCE block!\n" + str(err.args))
[docs] def adapt_energy_groups(self, energy_groups: Dict[int, int]):
"""
Change the Force groups.
Parameters
----------
residues : Dict[int, int]
the dictionary contains the forceGroups with the ID of the respective last atom.
{forceGroupID: lastAtom} = {1:12, 2:26, 3:128}
"""
self.NEGR = len(energy_groups)
self.NRE = [int(energy_groups[forceGroupID]) for forceGroupID in sorted(energy_groups)]
def __adapt_energy_groups(self, residues: Dict[str, Dict[int, int]]):
"""
Old REEDS option
adapt_energy_groups
This method is very "crude" and will put each residue into an own energy group.
Parameters
----------
residues : Dict[str, Dict[int, int]]
you can get this dict from cnf class.
Returns
-------
None
"""
# set Energy Group ammount
self.NEGR = len(residues) # total ammount of Engergy groups
if "SOLV" in residues and len(residues["SOLV"]) == 0:
self.NEGR -= 1
# make residues sortable,
dict_m = {}
solvent_names = ["WAT", "SOLV"]
for x in sorted(residues):
if type(residues[x]) == dict and x not in dict_m and x not in solvent_names:
dict_m.update(residues[x])
elif x in dict_m and not x == "SOLV" and not x == "SOL":
raise Exception("Found mutliple residues for the same residue id!")
# build up NRE
self.NRE = []
count = 0
for x in sorted(dict_m.keys()):
val = dict_m[x]
count += val
self.NRE.append(count)
# add solvents to NRE, but only if there are solvents
present_solvents = [x for x in residues if (x in solvent_names)]
if len(present_solvents) > 0:
if [isinstance(residues[x], dict) and residues[x] != 0 for x in present_solvents]:
self.NRE.append(sum(residues["SOLV"].values()) + count)
elif [isinstance(residues[x], int) and residues[x] != 0 for x in present_solvents]:
self.NRE.append(residues["SOLV"] + count)
[docs]class CONSTRAINT(_generic_imd_block):
"""CONSTRAINT block
This block is controlling constraining the atoms during a simulation.
Attributes
----------
NTC: int
NTCP: int
NTCP0: int
NTCS: int
NTCS0: int
"""
name: str = "CONSTRAINT"
NTC: int
NTCP: int
NTCP0: float
NTCS: int
NTCS0: float
_order = [[["NTC"], ["NTCP", "NTCP0(1)"], ["NTCS", "NTCS0(1)"]]]
[docs] def __init__(
self, NTC: int = 0, NTCP: int = 0, NTCP0: float = 0, NTCS: int = 0, NTCS0: float = 0, content: List[str] = None
):
"""
Args:
NTC:
NTCP:
NTCP0:
NTCS:
NTCS0:
"""
super().__init__(used=True, content=content)
if content is None:
self.NTC = int(NTC)
self.NTCP = int(NTCP)
self.NTCP0 = float(NTCP0)
self.NTCS = int(NTCS)
self.NTCS0 = float(NTCS0)
[docs]class PAIRLIST(_generic_imd_block):
"""PAIRLIST Block
This block is controlling the pairlist control.
Attributes
----------
ALGORITHM: int
standard(0) (gromos96 like pairlist)
grid(1) (md++ grid pairlist)
grid_cell(2) (creates a mask)
NSNB: int
frequency (number of steps) a pairlist is constructed
RCUTPL: float
short-range cut-off in twin-range
RCUTL: float
intermediate-range cut-off in twin-range
SIZE: str, float
grid cell size (or auto = 0.5 * RCUTP)
TYPE: str, bool
chargegoup(0) (chargegroup based cutoff)
atomic(1) (atom based cutoff)
"""
name: str = "PAIRLIST"
ALGORITHM: int
NSNB: int
RCUTPL: float
RCUTL: float
SIZE: Union[str, float]
TYPE: Union[str, bool]
_order = [[["ALGORITHM", "NSNB", "RCUTP", "RCUTL", "SIZE", "TYPE"]]]
[docs] def __init__(
self,
ALGORITHM: int = 0,
NSNB: int = 0,
RCUTP: float = 0,
RCUTL: float = 0,
SIZE: Union[str, float] = 0,
TYPE: Union[str, bool] = False,
content: List[str] = None,
):
"""
Args:
ALGORITHM:
NSNB:
RCUTP:
RCUTL:
SIZE:
TYPE:
"""
super().__init__(used=True, content=content)
if content is None:
self.ALGORITHM = int(ALGORITHM)
self.NSNB = int(NSNB)
self.RCUTP = float(RCUTP)
self.RCUTL = float(RCUTL)
self.SIZE = SIZE
self.TYPE = TYPE
[docs]class NONBONDED(_generic_imd_block):
"""NONBONDED block
This block is controlling the Nonbonded term evaluation
Attributes
----------
NLRELE: int
1-3 method to handle electrostatic interactions
-1 : reaction-field (LSERF compatibility mode)
0 : no electrostatic interactions
1 : reaction-field
2 : Ewald method
3 : P3M method
APPAK: float
>= 0.0 reaction-field inverse Debye screening length
RCRF: float
>= 0.0 reaction-field radius
EPSRF: float
= 0.0 || > 1.0 reaction-field permittivity
NSLFEXCL: bool
contribution of excluded atoms to reaction field false=off/true=on
NSHAPE: float
-1..10 lattice sum charge-shaping function
-1 : gaussian
0..10 : polynomial
ASHAPE: float
> 0.0 width of the lattice sum charge-shaping function
NA2CLC: int
0..4 controls evaluation of lattice sum A2 term
0 : A2 = A2~ = 0
1 : A2~ exact, A2 = A2~
2 : A2 numerical, A2~ = A2
3 : A2~ exact from Ewald or from mesh and atom coords, A2 numerical
4 : A2~ averaged from mesh only, A2 numerical
TOLA2: float
> 0.0 tolerance for numerical A2 evaluation
EPSLS: float
= 0.0 || > 1.0 lattice sum permittivity (0.0 = tinfoil)
NKX, NKY, NKZ: float
> 0 maximum absolute Ewald k-vector components
KCUT: float
> 0.0 Ewald k-space cutoff
NGX, NGY, NGZ: float
> 0 P3M number of grid points
NASORD: int
1..5 order of mesh charge assignment function
NFDORD: int
0..5 order of the mesh finite difference operator
0 : ik - differentiation
1..5 : finite differentiation
NALIAS: float
> 0 number of mesh alias vectors considered
NSPORD: float
order of SPME B-spline functions (not available)
NQEVAL: float
>= 0 controls accuracy reevaluation
0 : do not reevaluate
> 0 : evaluate every NQEVAL steps
FACCUR: float
> 0.0 rms force error threshold to recompute influence function
NRDGRD: bool
0,1 read influence function
0 : calculate influence function at simulation start up
1 : read influence function from file (not yet implemented)
NWRGRD: bool
0,1 write influence function
0 : do not write
1 : write at the end of the simulation (not yet implemented)
NLRLJ: bool
0,1 controls long-range Lennard-Jones corrections
0 : no corrections
1 : do corrections (not yet implemented)
SLVDNS: float
> 0.0 average solvent density for long-range LJ correction (ignored)
"""
name: str = "NONBONDED"
NLRELE: int
APPAK: float
RCRF: float
EPSRF: float
NSLFEXCL: bool
NSHAPE: int
ASHAPE: float
NA2CLC: int
TOLA2: str
EPSLS: float
NKX: int
NKY: int
NKZ: int
KCUT: float
NGX: int
NGY: int
NGZ: int
NASORD: int
NFDORD: int
NALIAS: int
NSPORD: int
NQEVAL: int
FACCUR: float
NRDGRD: bool
NWRGRD: bool
NLRLJ: bool
SLVDNS: float
_order = [
[
["NLRELE"],
["APPAK", "RCRF", "EPSRF", "NSLFEXCL"],
["NSHAPE", "ASHAPE", "NA2CLC", "TOLA2", "EPSLS"],
["NKX", "NKY", "NKZ", "KCUT"],
["NGX", "NGY", "NGZ", "NASORD", "NFDORD", "NALIAS", "NSPORD"],
["NQEVAL", "FACCUR", "NRDGRD", "NWRGRD"],
["NLRLJ", "SLVDNS"],
]
]
def __init__(
self,
NLRELE: int = 0,
APPAK: float = 0,
RCRF: float = 0,
EPSRF: float = 0,
NSLFEXCL: bool = False,
NSHAPE: int = 0,
ASHAPE: float = 0,
NA2CLC: int = 0,
TOLA2: str = 0,
EPSLS: float = 0,
NKX: int = 0,
NKY: int = 0,
NKZ: int = 0,
KCUT: float = 0,
NGX: int = 0,
NGY: int = 0,
NGZ: int = 0,
NASORD: int = 0,
NFDORD: int = 0,
NALIAS: int = 0,
NSPORD: int = 0,
NQEVAL: int = 0,
FACCUR: float = 0,
NRDGRD: bool = False,
NWRGRD: bool = False,
NLRLJ: bool = False,
SLVDNS: float = 0,
content: List[str] = None,
):
super().__init__(used=True, content=content)
if content is None:
self.NLRELE = NLRELE
self.APPAK = APPAK
self.RCRF = RCRF
self.EPSRF = EPSRF
self.NSLFEXCL = NSLFEXCL
self.NSHAPE = int(NSHAPE)
self.ASHAPE = ASHAPE
self.NA2CLC = int(NA2CLC)
self.TOLA2 = str(TOLA2)
self.EPSLS = EPSLS
self.NKX = int(NKX)
self.NKY = int(NKY)
self.NKZ = int(NKZ)
self.KCUT = KCUT
self.NGX = int(NGX)
self.NGY = int(NGY)
self.NGZ = int(NGZ)
self.NASORD = int(NASORD)
self.NFDORD = int(NFDORD)
self.NALIAS = int(NALIAS)
self.NSPORD = int(NSPORD)
self.NQEVAL = int(NQEVAL)
self.FACCUR = FACCUR
self.NRDGRD = bool(int(NRDGRD))
self.NWRGRD = bool(int(NWRGRD))
self.NLRLJ = bool(int(NLRLJ))
self.SLVDNS = SLVDNS
[docs]class INITIALISE(_generic_imd_block):
"""INITIALISE block
This block controls the Initialisation of a simulation.
Attributes
----------
NTIVEL: bool
0,1 controls generation of initial velocities
0: read from configuration (default)
1: generate from Maxell distribution at temperature TEMPI
NTISHK: int
0..3 controls shaking of initial configuration
0: no intial SHAKE (default)
1: initial SHAKE on coordinates only
2: initial SHAKE on velocities only
3: initial SHAKE on coordinates and velocities
NTINHT: bool
0,1 controls generation of initial Nose-Hoover chain variables
0: read from configuration (default)
1: reset variables to zero.
NTINHB: bool
0,1 controls generation of initial Nose-Hoover (chain) barostat variables
0: read from strartup file (if applicable) (default)
1: reset variables to zero
NTISHI: bool
0,1 controls initial setting for lattice shift vectors
0: read from configuration (default)
1: reset shifts to zero.
NTIRTC: bool
0,1 controls initial setting of positions and orientations for roto-translational constraints
0: read from configuration (default)
1: reset based on initial configuraion of startup file
NTICOM: int
0,1,2 controls initial removal of COM motion
0: no initial system COM motion removal (default)
1: initial COM translation is removed
2: initial COM rotation is removed
NTISTI: bool
0,1 controls generation of stochastic integrals
0: read stochastic integrals and IG from configuration (default)
1: set stochastic integrals to zero and use IG from here.
IG: int
random number generator seed
TEMPI: float
initial temperature
"""
name = "INITIALISE"
NTIVEL: bool
NTISHK: int
NTINHT: bool
NTINHB: bool
NTISHI: bool
NTIRTC: bool
NTICOM: int
NTISTI: bool
IG: int
TEMPI: float
_order = [[["NTIVEL", "NTISHK", "NTINHT", "NTINHB"], ["NTISHI", "NTIRTC", "NTICOM"], ["NTISTI"], ["IG", "TEMPI"]]]
[docs] def __init__(
self,
NTIVEL: bool = False,
NTISHK: int = 0,
NTINHT: bool = False,
NTINHB: bool = False,
NTISHI: bool = False,
NTIRTC: bool = False,
NTICOM: int = 0,
NTISTI: bool = False,
IG: int = 0,
TEMPI: float = 0,
content: List[str] = None,
):
"""
Args:
NTIVEL:
NTISHK:
NTINHT:
NTINHB:
NTISHI:
NTIRTC:
NTICOM:
NTISTI:
IG:
TEMPI:
"""
super().__init__(used=True, content=content)
if content is None:
self.NTIVEL = NTIVEL
self.NTISHK = NTISHK
self.NTINHT = NTINHT
self.NTINHB = NTINHB
self.NTISHI = NTISHI
self.NTIRTC = NTIRTC
self.NTICOM = NTICOM
self.NTISTI = NTISTI
self.IG = IG
self.TEMPI = TEMPI
[docs]class COMTRANSROT(_generic_imd_block):
"""COMTRANSROT block
This block controls the center of mass translation and rotation removal. (flying ice cube problem)
Attributes
----------
NSCM : int
controls system centre-of-mass (com) motion removal
0: no com motion removal (default)
< 0: com translation and rotation are removed every abs(NSCM) steps.
> 0: com translation is removed every NSCM steps.
"""
name: str = "COMTRANSROT"
NSCM: int
_order = [[["NSCM"]]]
def __init__(self, NSCM: int = 0, content: List[str] = None):
super().__init__(used=True, content=content)
if content is None:
self.NSCM = NSCM
[docs]class QMMM(_generic_imd_block):
"""QMMM Block
The QMMM block describes how to treat an embedded QM zone
Attributes
----------
NTQMMM: int
Define if QM/MM is applied (1) or not (0, default)
NTQMSW: int
QM software package to use. Available options are MNDO (0, default), Turbomole (1), DFTB (2), MOPAC (3), ORCA (4), and XTB (5)
RCUTQ: float
Cutoff for inclusion of MM charge groups. If the value is 0, all particles are considered.
NTWQMMM: int
Write QM/MM related data to special trajectory every NTWQMMM step. Switch off with 0.
QMLJ: int
Define if LJ interaction is activated in the QM zone (1) or not (0).
MMSCAL: float
Scale MM charges with (2/pi)*atan(x*(r_{mm}-r_{mm})). Values > 0.0 describe the scaling factor, values < 0.0 turn off scaling (default).
"""
name: str = "QMMM"
# fields
NTQMMM: int # Define if QM/MM is applied (1) or not (0, default)
NTQMSW: int # QM software package to use. Available options are MNDO (0, default), Turbomole (1), DFTB (2), MOPAC (3), ORCA (4), and XTB (5)
RCUTQ: float # Cutoff for inclusion of MM charge groups. If the value is 0, all particles are considered.
NTWQMMM: int # Write QM/MM related data to special trajectory every NTWQMMM step. Switch off with 0.
QMLJ: int # Define if LJ interaction is activated in the QM zone (1) or not (0).
MMSCAL: float # Scale MM charges with (2/pi)*atan(x*(r_{mm}-r_{mm})). Values > 0.0 describe the scaling factor, values < 0.0 turn off scaling (default).
_order = [[["NTQMMM", "NTQMSW", "RCUTQ", "NTWQMMM", "QMLJ", "MMSCAL"]]]
def __init__(
self,
NTQMMM: int = 0,
NTQMSW: int = 0,
RCUTQ: float = 0.0,
NTWQMMM: int = 0,
QMLJ: int = 0,
MMSCAL: float = -1.0,
content: List[str] = None,
):
super().__init__(used=True, content=content)
if content is None:
self.NTQMMM = int(NTQMMM)
self.NTQMSW = int(NTQMSW)
self.RCUTQ = float(RCUTQ)
self.NTWQMMM = int(NTWQMMM)
self.QMLJ = int(QMLJ)
self.MMSCAL = float(MMSCAL)
[docs]class EDS(_generic_imd_block):
"""EDS block
This block is used in an EDS simulation.
Attributes
-----------
NUMSTATES: int
EDS-States
S: float
smoothness parameter
EIR: List[float]
energy offsets
EDS: bool, optional
turn on EDS_simulation
ALPHLJ: float, optional
ALPHC: float, optional
FUNCTIONAL: int, optional
1: Single s Hamiltonian (default)
2: Hamiltonian with NUMSTATES*(NUMSTATES-1)/2 (pairwise) S parameters ==> changes type of S
3: Hamiltonian with (NUMSTATES-1) S parameters ==> changes type of S
"""
_order = [[["EDS"], ["ALPHLJ", "ALPHC"], ["FUNCTIONAL FORM"], ["NUMSTATES"], ["S"], ["EIR"]]]
def __init__(
self,
NUMSTATES: int = 0,
S: float = 0,
EIR: List[float] = [],
EDS: bool = 1,
ALPHLJ: float = 0.0,
ALPHC: float = 0.0,
FUNCTIONAL: int = 1,
content: List[str] = None,
):
super().__init__(used=True, content=content)
if content is None:
self.name = "EDS"
self.EDS = EDS
self.ALPHLJ = ALPHLJ
self.ALPHC = ALPHC
self.FUNCTIONAL = FUNCTIONAL
self.NUMSTATES = NUMSTATES
self.S = S
self.EIR = EIR
[docs] def read_content_from_str(self, content: List[str]):
try:
setattr(self, "EDS", int(content[1].split()[0]))
setattr(self, "ALPHLJ", float(content[3].split()[0]))
setattr(self, "ALPHC", float(content[3].split()[1]))
setattr(self, "FUNCTIONAL", int(content[5].split()[0]))
setattr(self, "NUMSTATES", int(content[7].split()[0]))
setattr(self, "S", list(map(float, content[9].split())))
setattr(self, "EIR", list(map(float, content[11].split())))
except Exception as err:
raise IOError("Could not parse block from str - " + __class__.__name__ + "\n" + str(err.args))
[docs]class DISTANCERES(_generic_imd_block):
"""DISTANCERES Block"""
name = "DISTANCERES"
NTDIR: int
NTDIRA: int
CDIR: int
DIR0: int
TAUDIR: int
FORCESCALE: int
VDIR: int
NTWDIR: int
_order = [[["NTDIR", "NTDIRA", "CDIR", "DIR0", "TAUDIR", "FORCESCALE", "VDIR", "NTWDIR"]]]
[docs] def __init__(
self,
NTDIR: int = 0,
NTDIRA: int = 0,
CDIR: int = 0,
DIR0: int = 0,
TAUDIR: int = 1,
FORCESCALE: int = 0,
VDIR: int = 0,
NTWDIR: int = 0,
content: List[str] = None,
):
"""
Args:
NTDIR:
NTDIRA:
CDIR:
DIR0:
TAUDIR:
FORCESCALE:
VDIR:
NTWDIR:
"""
super().__init__(used=True, content=content)
if content is None:
self.NTDIR = NTDIR
self.NTDIRA = NTDIRA
self.CDIR = CDIR
self.DIR0 = DIR0
self.TAUDIR = TAUDIR
self.FORCESCALE = FORCESCALE
self.VDIR = VDIR
self.NTWDIR = NTWDIR
[docs]class POSITIONRES(_generic_imd_block):
"""POSITIONRES block
This block allows the managment of the Position Restraints during the Simulation.
Attributes
----------
NTPOR:
0..3 controls atom positions re(con)straining.
0: no position re(con)straints (default)
1: restraining with force constant CPOR
2: restraining with force constant CPOR weighted by atomic B-factors
3: constraining
NTPORB:
0,1 controls reading of reference positions and B-factors
0: read reference positions from startup file.
1: read reference positions and B-factors from special file
NTPORS:
0,1 controls scaling of reference positions upon pressure scaling
0: do not scale reference positions
1: scale reference positions
CPOR:
>= 0 position restraining force constant
"""
name = "POSITIONRES"
NTPOR: int
NTPORB: int
NTPORS: int
CPOR: int
_order = [[["NTPOR", "NTPORB", "NTPORS", "CPOR"]]]
def __init__(self, NTPOR: int = 0, NTPORB: int = 0, NTPORS: int = 0, CPOR: int = 0, content: List[str] = None):
super().__init__(used=True, content=content)
if content is None:
self.NTPOR = NTPOR
self.NTPORB = NTPORB
self.NTPORS = NTPORS
self.CPOR = CPOR
[docs]class PRINTOUT(_generic_imd_block):
"""PRINTOUT block
This Block manages the output frequency into the .omd/std-out file.
Attributes
----------
NTPR: int
print out energies, etc. every NTPR steps
NTPP: int
=1 perform dihedral angle transition monitoring
"""
name: str = "PRINTOUT"
NTPR: int
NTPP: int
_order = [[["NTPR", "NTPP"]]]
def __init__(self, NTPR: int = 0, NTPP: int = 0, content: List[str] = None):
super().__init__(used=True, content=content)
if content is None:
self.name = "PRINTOUT"
self.NTPR = NTPR
self.NTPP = NTPP
[docs]class ENERGYMIN(_generic_imd_block):
"""ENERGYMIN block
This block takes care of managing the Energyminimization controls.
Attributes
----------
NTEM: int
controls energy minimisation mode.
0: do not do energy minimisation (default)
1: steepest-descent minimisation
2: Fletcher-Reeves conjugate-gradient minimisation
3: Polak-Ribiere conjugate-gradient minimisation
NCYC: int
>0 number of steps before resetting the conjugate-gradient search direction
=0 reset only if the energy grows in the search direction
DELE: float
>0.0 energy threshold for convergence
>0.0 (conjugate-gradient) RMS force threshold for convergence
DX0: float
>0.0 initial step size
DXM: float
>0.0 maximum step size
NMIN: float
>0 minimum number of minimisation steps
FLIM: float
>=0.0 limit force to maximum value (FLIM > 0.0 is not recommended)
"""
name = "ENERGYMIN"
NTEM: int
NCYC: int
DELE: float
DX0: float
DXM: float
NMIN: int
FLIM: float
_order = [["NTEM NCYC DELE DX0 DXM NMIN FLIM".split()]]
def __init__(
self,
NTEM: int = 0,
NCYC: int = 0,
DELE: float = 0,
DX0: float = 0,
DXM: float = 0,
NMIN: int = 0,
FLIM: float = 0,
content: List[str] = None,
):
super().__init__(used=True, content=content)
if content is None:
self.NTEM = NTEM
self.NCYC = NCYC
self.DELE = DELE
self.DX0 = DX0
self.DXM = DXM
self.NMIN = NMIN
self.FLIM = FLIM
[docs]class WRITETRAJ(_generic_imd_block):
"""WRITETRAJ
The WRITETRAJ Block manages the output frequency of GROMOS. Here you can determine how often a file should be written.
Attributes
----------
NTWX: int
controls writing of coordinate trajectory
0: no coordinate trajectory is written (default)
>0: write solute and solvent coordinates every NTWX steps
<0: write solute coordinates every |NTWX| steps
NTWSE: int
>= 0 selection criteria for coordinate trajectory writing
0: write normal coordinate trajectory
>0: write minimum-energy coordinate and energy trajectory (based on the
energy entry selected by NTWSE and as blocks of length NTWX)
(see configuration/energy.cc or ene_ana library for indices)
NTWV: int
controls writing of velocity trajectory
0: no velocity trajectory is written (default)
>0: write solute and solvent velocities every NTWV steps
<0: write solute velocities every |NTWV| steps
NTWF: int
controls writing of force trajectory
0: no force trajectory is written (default)
>0: write solute and solvent forces every NTWF steps
<0: write solute forces every |NTWF| steps
NTWE: int
>= 0 controls writing of energy trajectory
0: no energy trajectory is written (default)
>0: write energy trajectory every NTWE steps
NTWG: int
>= 0 controls writing of free energy trajectory
0: no free energy trajectory is written (default)
>0: write free energy trajectory every NTWG steps
NTWB: int
>= 0 controls writing of block-averaged energy trajectory
0: no block averaged energy trajectory is written (default)
>0: write block-averaged energy variables every |NTWB| steps
(and free energies if NTWG > 0) trajectory
"""
name = "WRITETRAJ"
NTWX: int
NTWSE: int
NTWV: int
NTWF: int
NTWE: int
NTWG: int
NTWB: int
_order = [[["NTWX", "NTWSE", "NTWV", "NTWF", "NTWE", "NTWG", "NTWB"]]]
[docs] def __init__(
self,
NTWX: int = 0,
NTWSE: int = 0,
NTWV: int = 0,
NTWF: int = 0,
NTWE: int = 0,
NTWG: int = 0,
NTWB: int = 0,
content: List[str] = None,
):
"""
Args:
NTWX:
NTWSE:
NTWV:
NTWF:
NTWE:
NTWG:
NTWB:
"""
super().__init__(used=True, content=content)
if content is None:
self.NTWX = int(NTWX)
self.NTWSE = int(NTWSE)
self.NTWV = int(NTWV)
self.NTWF = int(NTWF)
self.NTWE = int(NTWE)
self.NTWG = int(NTWG)
self.NTWB = int(NTWB)
[docs]class AMBER(_generic_imd_block):
"""AMBER block
Attributes
----------
Amber: bool
AMBSCAL: float
"""
name: str = "AMBER"
Amber: bool
AMBSCAL: float
_order = [[["AMBER", "AMBSCAL"]]]
def __init__(self, AMBER: int = 0, AMBSCAL: int = 0, content: List[str] = None):
super().__init__(used=True, content=content)
if content is None:
self.name = "AMBER"
self.AMBER = AMBER
self.AMBSCAL = AMBSCAL
[docs]class INNERLOOP(_generic_imd_block):
"""INNERLOOP block
Attributes
----------
NTILM: int
NTILS: int
NGPUS: int
"""
name = "INNERLOOP"
NTILM: int
NTILS: int
NGPUS: int
_order = [[["NTILM", "NTILS", "NGPUS"]]]
def __init__(self, NTILM: int = 0, NTILS: int = 0, NGPUS: int = 0, content: List[str] = None):
super().__init__(used=True, content=content)
if content is None:
self.NTILM = NTILM
self.NTILS = NTILS
self.NGPUS = NGPUS
[docs]class LAMBDA(_generic_imd_block):
"""
LAMBDA block
This block is controlling the perturbation for free energy calculations.
Attributes
----------
NTIL: int
NTLI(1): int or List[int]
NILG1(1): int or List[int]
NILG2(1): int or List[int]
ALI(1): float or List[float]
BLI(1): float or List[float]
CLI(1): float or List[float]
DLI(1): float or List[float]
ELI(1): float or List[float]
"""
name: str = "LAMBDA"
NTIL: int
NTLI: List[int]
NILG1: List[int]
NILG2: List[int]
ALI: List[float]
BLI: List[float]
CLI: List[float]
DLI: List[float]
ELI: List[float]
_order: List[List[str]] = [
[
["NTIL"],
["NTLI(1..)", "NILG1(1..)", "NILG2(1..)", "ALI(1..)", "BLI(1;;)", "CLI(1;;)", "DLI(1;;)", "ELI(1;;)"],
]
]
def __init__(
self,
NTIL: int = 0,
NTLI: List[int] = [],
NILG1: List[int] = [],
NILG2: List[int] = [],
ALI: List[float] = [],
BLI: List[float] = [],
CLI: List[float] = [],
DLI: List[float] = [],
ELI: List[float] = [],
content: List[str] = None,
):
super().__init__(used=True, content=content)
if content is None:
self.NTIL = NTIL
self.NTLI = NTLI
self.NILG1 = NILG1
self.NILG2 = NILG2
self.ALI = ALI
self.BLI = BLI
self.CLI = CLI
self.DLI = DLI
self.ELI = ELI
if not len(NTLI) == len(NILG1):
warnings.warn("Warning in LAMBDA Block. There must be the same number of NTLI and NILG1 parameters.")
if not len(NTLI) == len(NILG2):
warnings.warn("Warning in LAMBDA Block. There must be the same number of NTLI and NILG2 parameters.")
if not len(NTLI) == len(ALI):
warnings.warn("Warning in LAMBDA Block. There must be the same number of NTLI and ALI parameters.")
if not len(NTLI) == len(BLI):
warnings.warn("Warning in LAMBDA Block. There must be the same number of NTLI and BLI parameters.")
if not len(NTLI) == len(CLI):
warnings.warn("Warning in LAMBDA Block. There must be the same number of NTLI and CLI parameters.")
if not len(NTLI) == len(DLI):
warnings.warn("Warning in LAMBDA Block. There must be the same number of NTLI and DLI parameters.")
if not len(NTLI) == len(ELI):
warnings.warn("Warning in LAMBDA Block. There must be the same number of NTLI and ELI parameters.")
[docs]class ROTTRANS(_generic_imd_block):
"""Roto-translational block
This block is for roto-translational constraints.
Note: use either centre of mass removal or roto-translational constraints but not both!
Attributes
----------
RTC: int
turn roto-translational constraints on (1)
RTCLAST: int
last atom of subset to be roto-translationally constrained
"""
name: str = "ROTTRANS"
RTC: int
RTCLAST: int
_order = [[["RTC", "RTCLAST"]]]
def __init__(self, RTC: int = 0, RTCLAST: int = 0, content: List[str] = None):
super().__init__(used=True, content=content)
if content is None:
self.RTC = RTC
self.RTCLAST = RTCLAST
[docs]class RANDOMNUMBERS(_generic_imd_block):
"""
Random Numbers Block
"""
name: str = "RANDOMNUMBERS"
NTRNG: int
NTGSL: int
_order = [[["NTRNG"], ["NTGSL"]]]
def __init__(self, NTRNG: int = 0, NTGSL: int = 0, content: List[str] = None):
super().__init__(used=True, content=content)
if content is None:
self.NTRNG = int(NTRNG)
self.NTGSL = int(NTGSL)