####################################################################### BioSimSpace: Making biomolecular simulation a breeze!## Copyright: 2017-2024## Authors: Lester Hedges <lester.hedges@gmail.com>## BioSimSpace is free software: you can redistribute it and/or modify# it under the terms of the GNU General Public License as published by# the Free Software Foundation, either version 3 of the License, or# (at your option) any later version.## BioSimSpace is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the# GNU General Public License for more details.## You should have received a copy of the GNU General Public License# along with BioSimSpace. If not, see <http://www.gnu.org/licenses/>.#####################################################################"""Functionality for generating configuration files for AMBER."""__author__="Lester Hedges"__email__="lester.hedges@gmail.com"__all__=["Gromacs"]importmathas_mathimportwarningsas_warningsfrom..importProtocolas_Protocolfrom..Protocol._free_energy_mixinimport_FreeEnergyMixinfrom..Protocol._position_restraint_mixinimport_PositionRestraintMixinfrom._configimportConfigas_Config
[docs]classGromacs(_Config):"""A class for generating configuration files for GROMACS."""
[docs]def__init__(self,system,protocol,property_map={}):""" Constructor. Parameters ---------- system : :class:`System <BioSimSpace._SireWrappers.System>` The molecular system. protocol : :class:`Protocol <BioSimSpace.Protocol>` The protocol for the process. property_map : dict A dictionary that maps system "properties" to their user defined values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """# Call the base class constructor.super().__init__(system,protocol,property_map=property_map)
[docs]defcreateConfig(self,version=None,extra_options={},extra_lines=[]):""" Create the list of configuration strings. version : float The GROMACS version. extra_options : dict A dictionary containing extra options. Overrides the defaults generated by the protocol. extra_lines : [str] A list of extra lines to put at the end of the configuration file. Returns ------- config : [str] The list of AMBER format configuration strings. """# Validate input.ifversionandnotisinstance(version,float):raiseTypeError("'version' must be of type 'float'.")ifnotisinstance(extra_options,dict):raiseTypeError("'extra_options' must be of type 'dict'.")else:keys=extra_options.keys()ifnotall(isinstance(k,str)forkinkeys):raiseTypeError("Keys of 'extra_options' must be of type 'str'.")ifnotisinstance(extra_lines,list):raiseTypeError("'extra_lines' must be of type 'list'.")else:ifnotall(isinstance(line,str)forlineinextra_lines):raiseTypeError("Lines in 'extra_lines' must be of type 'str'.")# Make sure the report interval is a multiple of nstcalcenergy.ifisinstance(self._protocol,_FreeEnergyMixin):nstcalcenergy=250else:nstcalcenergy=100report_interval=self.reportInterval()ifreport_interval%nstcalcenergy!=0:report_interval=nstcalcenergy*_math.ceil(report_interval/nstcalcenergy)# Define some miscellaneous defaults.protocol_dict={# Interval between writing to the log file."nstlog":report_interval,# Interval between writing to the energy file."nstenergy":report_interval,# Interval between writing to the trajectory file."nstxout-compressed":self.restartInterval(),}# Minimisation.ifisinstance(self._protocol,_Protocol.Minimisation):protocol_dict["integrator"]="steep"else:# Timestep in picosecondstimestep=self._protocol.getTimeStep().picoseconds().value()# Integration time step.protocol_dict["dt"]=f"{timestep:.3f}"# Number of integration steps.protocol_dict["nsteps"]=self.steps()# Constraints.ifnotisinstance(self._protocol,_Protocol.Minimisation):# Rigid bonded hydrogens.protocol_dict["constraints"]="h-bonds"# Linear constraint solver.protocol_dict["constraint-algorithm"]="LINCS"# Periodic boundary conditions.# Simulate a fully periodic box.protocol_dict["pbc"]="xyz"# Use Verlet pair lists.protocol_dict["cutoff-scheme"]="Verlet"ifself.hasBox(self._system,self._property_map)andself.hasWater(self._system):# Use a grid to search for neighbours.protocol_dict["ns-type"]="grid"# Rebuild neighbour list every 20 steps.protocol_dict["nstlist"]="20"# Set short-range cutoff.protocol_dict["rlist"]="0.8"# Set van der Waals cutoff.protocol_dict["rvdw"]="0.8"# Set Coulomb cutoff.protocol_dict["rcoulomb"]="0.8"# Fast smooth Particle-Mesh Ewald.protocol_dict["coulombtype"]="PME"# Dispersion corrections for energy and pressure.protocol_dict["DispCorr"]="EnerPres"else:# Perform vacuum simulations by implementing pseudo-PBC conditions,# i.e. run calculation in a near-infinite box (333.3 nm).# c.f.: https://pubmed.ncbi.nlm.nih.gov/29678588# Single neighbour list (all particles interact).protocol_dict["nstlist"]="1"# "Infinite" short-range cutoff.protocol_dict["rlist"]="333.3"# "Infinite" van der Waals cutoff.protocol_dict["rvdw"]="333.3"# "Infinite" Coulomb cutoff.protocol_dict["rcoulomb"]="333.3"# Plain cut-off.protocol_dict["coulombtype"]="Cut-off"# Twin-range van der Waals cut-off.protocol_dict["vdwtype"]="Cut-off"# Position restraints.ifisinstance(self._protocol,_PositionRestraintMixin):# Note that constraints will be defined by the GROMACS process.protocol_dict["refcoord-scaling"]="com"# Pressure control.ifnotisinstance(self._protocol,_Protocol.Minimisation):ifself._protocol.getPressure()isnotNone:# Don't use barostat for vacuum simulations.ifself.hasBox(self._system,self._property_map)andself.hasWater(self._system):# Barostat type.ifversionandversion>=2021:protocol_dict["pcoupl"]="c-rescale"else:protocol_dict["pcoupl"]="berendsen"# 1ps time constant for pressure coupling.protocol_dict["tau-p"]=1# Pressure in bar.protocol_dict["ref-p"]=(f"{self._protocol.getPressure().bar().value():.5f}")# Compressibility of water.protocol_dict["compressibility"]="4.5e-5"else:_warnings.warn("Cannot use a barostat for a vacuum or non-periodic simulation")# Temperature control.ifnotisinstance(self._protocol,_Protocol.Minimisation):ifisinstance(self._protocol,_FreeEnergyMixin):# Langevin dynamics.protocol_dict["integrator"]="sd"else:# Leap-frog molecular dynamics.protocol_dict["integrator"]="md"# Temperature coupling using velocity rescaling with a stochastic term.protocol_dict["tcoupl"]="v-rescale"# A single temperature group for the entire system.protocol_dict["tc-grps"]="system"# Thermostat coupling frequency (ps).protocol_dict["tau-t"]="{:.5f}".format(self._protocol.getThermostatTimeConstant().picoseconds().value())ifisinstance(self._protocol,_Protocol.Equilibration):ifself._protocol.isConstantTemp():temp=("%.2f"%self._protocol.getStartTemperature().kelvin().value())protocol_dict["ref-t"]=tempprotocol_dict["gen-vel"]="yes"protocol_dict["gen-temp"]=tempelse:# still need a reference temperature for each group,# even when heating/coolingprotocol_dict["ref-t"]=("%.2f"%self._protocol.getEndTemperature().kelvin().value())# Work out the final time of the simulation.timestep=self._protocol.getTimeStep().picoseconds().value()end_time=_math.floor(timestep*self.steps())# Single sequence of annealing points.protocol_dict["annealing"]="single"# Two annealing points for "system" temperature group.protocol_dict["annealing-npoints"]=2# Linearly change temperature between start and end times.protocol_dict["annealing-time"]="0 %d"%end_timeprotocol_dict["annealing-temp"]="%.2f%.2f"%(self._protocol.getStartTemperature().kelvin().value(),self._protocol.getEndTemperature().kelvin().value(),)else:# Fixed temperature.protocol_dict["ref-t"]=("%.2f"%self._protocol.getTemperature().kelvin().value())# Free energies.ifisinstance(self._protocol,_FreeEnergyMixin):# Extract the lambda array.lam_vals=self._protocol.getLambdaValues()# Determine the index of the lambda value.idx=self._protocol.getLambdaIndex()# Free energy mode.protocol_dict["free-energy"]="yes"# Initial lambda state.protocol_dict["init-lambda-state"]=idx# Lambda value array.protocol_dict["fep-lambdas"]=" ".join([str(x)forxinlam_vals])# All interactions on at lambda = 0protocol_dict["couple-lambda0"]="vdw-q"# All interactions on at lambda = 1protocol_dict["couple-lambda1"]="vdw-q"# Write all lambda values.protocol_dict["calc-lambda-neighbors"]=-1# Calculate energies every 250 steps.protocol_dict["nstcalcenergy"]=250# Write gradients every 250 steps.protocol_dict["nstdhdl"]=250# Put everything together in a line-by-line format.total_dict={**protocol_dict,**extra_options}total_lines=[f"{k} = {v}"fork,vintotal_dict.items()ifvisnotNone]+extra_linesreturntotal_lines