def add_ions(neutral=True, conc=0, pname='NA', nname='CL'): if (not neutral) and (conc == 0): utils.update("add_ions", "no ions will be added...") return if neutral: utils.update("add_ions", "will add ions ({}/{}) to neutralize the system...".format(pname, nname)) if conc > 0: utils.update("add_ions", "will add ions ({}/{}) for target concentration = {} mmol/ml...".format(pname, nname, conc)) # Generate IONS.mdp (just a dummy required). os.system('touch IONS.mdp') # Add ion topology to topol.top. topol.add_mol("{0}.ff/ions.itp".format(universe.get('d_modelFF')), "Include ion topology") # Run gmx grompp and genion. utils.update("add_ions", "running gmx grompp and genion to add ions...") os.system("gmx grompp -f IONS.mdp -c {0} -p topol.top -o IONS.tpr >> builder.log 2>&1".format(universe.get('d_nameList')[-1])) if neutral: os.system("gmx genion -s IONS.tpr -o {0}_ION.pdb -p topol.top -pname {1} -nname {2} -conc {3} -neutral >> builder.log 2>&1 << EOF\nSOL\nEOF".format(universe.get('d_pdbName'), pname, nname, conc)) else: os.system("gmx genion -s IONS.tpr -o {0}_ION.pdb -p topol.top -pname {1} -nname {2} -conc {3} >> builder.log 2>&1 << EOF\nSOL\nEOF".format(universe.get('d_pdbName'), pname, nname, conc)) # To update d_residues. load("{0}_ION.pdb".format(universe.get('d_pdbName'))) # To update d_nameList. utils.add_to_nameList("{0}_ION.pdb".format(universe.get('d_pdbName')))
def run(gmxPath="/usr/local/gromacs", options=""): # If we do a non-constant-pH simulation, gen_constantph will not make index.ndx, # so do it here. if not os.path.isfile('index.ndx'): utils.generate_index() # User update: if options == "": utils.update("run", "gmxPath={0}".format(gmxPath)) else: utils.update( "run", "gmxPath={0}, additional options= {1}".format(gmxPath, options)) with open("run.sh", 'w') as file: file.write("#!/bin/bash\n\n") file.write("# Gromacs version to use:\n") file.write("source {0}/bin/GMXRC\n\n".format(gmxPath)) file.write( "gmx grompp -f MD.mdp -c {0} -p topol.top -n index.ndx -o MD.tpr -r {0}\n" .format(universe.get('d_nameList')[-1])) file.write( "gmx mdrun -deffnm MD -v -c {0}_MD.pdb -x MD.xtc {1}\n".format( universe.get('d_pdbName'), options)) os.system("chmod +x run.sh")
def process(fname, d_model=1, d_ALI='A', d_chain=[], resetResId=False): basename = os.path.basename(fname) universe.add('d_pdbName', basename[0:len(basename) - 4]) universe.add('d_model', d_model) universe.add('d_ALI', d_ALI) load(fname, d_model, d_ALI, d_chain) # Update d_chain from [] to a list of actual chains used: d_chain = [] for residue in universe.get('d_residues'): d_chain.append(residue.d_chain) d_chain = list(set(d_chain)) d_chain.sort() universe.add('d_chain', d_chain) utils.update( "process", 'file={0}, MODEL#={1}, ALI={2}, chain(s)={3}...'.format( fname, d_model, d_ALI, d_chain)) # Write processed.pdb to file: utils.update( "process", "writing processed .pdb file to {}_PR1.pdb...".format( universe.get('d_pdbName'))) write("{0}_PR1.pdb".format(universe.get('d_pdbName')))
def write(name): with open(name, 'w') as file: if universe.has('d_title'): file.write("TITLE {0}\n".format(universe.get('d_title'))) if universe.has('d_box'): file.write("CRYST1{0}\n".format(universe.get('d_box'))) file.write("MODEL {:8d}\n".format(universe.get('d_model'))) atomNumber = 1 for residue in universe.get('d_residues'): for idx in range(0, len(residue.d_atoms)): file.write( "{:6s}{:5d} {:^4s}{:1s}{:4s}{:1s}{:4d}{:1s} {:8.3f}{:8.3f}{:8.3f}\n" .format('ATOM', atomNumber, residue.d_atoms[idx], residue.d_ali[idx], residue.d_resname, residue.d_chain, residue.d_resid, '', residue.d_x[idx], residue.d_y[idx], residue.d_z[idx])) atomNumber += 1 file.write("TER\nENDMDL\n") utils.add_to_nameList(name)
def restrain_dihedrals(resName, atomNameList, Type, phi, dphi, fc): utils.update("restrain_dihedrals", "will add restraints for {0} (all chains)...".format(resName)) # Every chain has its own .itp file, so we loop through every file: for letter in universe.get('d_chain'): # This is to make sure we don't have multiple headers when we add multiple different restraints. first = False if not "[ dihedral_restraints ]" in open("topol_Protein_chain_{0}.itp".format(letter)).read(): first = True # Append to the end of relevant .itp file: with open("topol_Protein_chain_{0}.itp".format(letter), 'a') as file: if first: file.write("[ dihedral_restraints ]\n") file.write("; ai aj ak al type phi dphi fc\n") # Atomcount resets for every separate .itp file. atomCount = 0 for residue in universe.get('d_residues'): # Dictionary resets for every residue, as we can of course have # multiple ASPs or GLUs in one chain. dictionary = {} for atom in residue.d_atoms: # Only increase atomcount when we read the relevant chain: if residue.d_chain == letter: atomCount += 1 if residue.d_resname == resName and atom in atomNameList: dictionary[atom] = atomCount if len(dictionary) == 4: utils.update("restrain_dihedrals", "adding restraints for chain {0} {1}-{2}...".format(residue.d_chain, resName, residue.d_resid)) for atom in atomNameList: file.write("{:<6d} ".format(dictionary[atom])) file.write(" {} {} {} {}\n".format(Type, phi, dphi, fc))
def reset(): utils.update("reset", "writing reset.sh...") with open("reset.sh", "w+") as file: file.write("#!/bin/bash\n\n") file.write("if [ -f \"%s_MD.pdb\" ]\nthen\n" % universe.get('d_pdbName')) file.write("\tread -p \"Warning: simulation has finished. Proceed? (y)\" var\n") file.write("else\n") file.write("\trm -rf \\_\\_py* charmm*\n") file.write("\trm -f *.itp *.top *.mdp *.tpr *.log *.ndx *.edr *.trr *.xtc *.cpt *.dat *.pdf *.xvg\n") file.write("\trm -f \\#*\\# EM.pdb NVT.pdb NPT.pdb MD.pdb \n") file.write("\trm -f step*.pdb buffer.pdb %s_*.pdb\n" % universe.get('d_pdbName')) file.write("\trm -f run.sh reset.sh jobscript.sh universe\n") file.write("fi\n\n") file.write("if [ \"${var}\" = \"y\" ]\nthen\n") file.write("\trm -rf \\_\\_py* charmm*\n") file.write("\trm -f *.itp *.top *.mdp *.tpr *.log *.ndx *.edr *.trr *.xtc *.cpt *.dat *.pdf *.xvg\n") file.write("\trm -f \\#*\\# EM.pdb NVT.pdb NPT.pdb MD.pdb \n") file.write("\trm -f step*.pdb buffer.pdb %s_*.pdb\n" % universe.get('d_pdbName')) file.write("\trm -f run.sh reset.sh jobscript.sh universe\n") file.write("fi\n\n") os.system("chmod +x reset.sh")
def jobscript(jobName, jobTime, nodes, ntasks, queue): utils.update( "jobscript", "jobName={0}, jobTime={1}(hrs), nodes={2}, ntasks={3}, queue={4}...". format(jobName, jobTime, nodes, ntasks, queue)) file = open("jobscript.sh", 'w') def writeHead(param, value): file.write("#SBATCH --%s=%s\n" % (param, value)) def moduleLoad(value): file.write("module load {0}\n".format(value)) file.write("#!/bin/bash\n\n") writeHead("time", "%d-%.2d:00:00" % (int(jobTime / 24), jobTime % 24)) writeHead("nodes", nodes) # writeHead("ntasks", ntasks) writeHead("partition", queue) writeHead("job-name", jobName) writeHead("mail-user", "*****@*****.**") writeHead("mail-type", "ALL") file.write("#SBATCH -C gpu --gres=gpu:1\n\n") moduleLoad("cmake/latest") moduleLoad("gcc/7.4") moduleLoad("cuda/10.2") file.write('\n') if universe.get('ph_constantpH'): file.write( "# compile our custom Gromacs version on cluster backend node\n") file.write("mkdir build\n") file.write("cd build\n") file.write( "CC=gcc-7 CXX=g++-7 cmake ~/gromacs-constantph -DGMX_USE_RDTSCP=ON -DCMAKE_INSTALL_PREFIX=${PWD}/.. -DGMX_BUILD_OWN_FFTW=ON -DGMX_GPU=CUDA\n" ) file.write("make -j 12\n") file.write("make install -j 12\n") file.write("cd ..\n") file.write("rm -r build\n") file.write("source ${PWD}/bin/GMXRC\n\n") else: file.write("module load gromacs/2021.1\n\n") file.write( "gmx grompp -f MD.mdp -c {0} -p topol.top -n index.ndx -o MD.tpr -r {0}\n" .format(universe.get('d_nameList')[-1])) if universe.get('ph_constantpH'): file.write( "gmx mdrun -deffnm MD -c {0}_MD.pdb -x MD.xtc -pme cpu -ntmpi 1\n". format(universe.get('d_pdbName'))) else: file.write("gmx mdrun -deffnm MD -c {0}_MD.pdb -x MD.xtc\n".format( universe.get('d_pdbName')))
def add_water(): utils.update("add_water", "running gmx solvate...") os.system("gmx solvate -cp {0} -o {1}_SOL.pdb >> builder.log 2>&1".format(universe.get('d_nameList')[-1], universe.get('d_pdbName'))) # To update d_residues. load("{0}_SOL.pdb".format(universe.get('d_pdbName'))) # To update topol.top. topol.add_mol("{0}.ff/{1}.itp".format(universe.get('d_modelFF'), universe.get('d_modelWater')), "Include water topology", "SOL", countRes('SOL')) # To update d_nameList. utils.add_to_nameList("{0}_SOL.pdb".format(universe.get('d_pdbName')))
def energy_minimize(): gen_mdp('EM') utils.update("energy_minimize", "running gmx grompp and mdrun for energy minimization...") os.system( "gmx grompp -f EM.mdp -c {0} -p topol.top -o EM.tpr -r {0} >> builder.log 2>&1" .format(universe.get('d_nameList')[-1])) os.system("gmx mdrun -deffnm EM -c {0}_EM.pdb >> builder.log 2>&1".format( universe.get('d_pdbName'))) utils.add_to_nameList("{0}_EM.pdb".format(universe.get('d_pdbName')))
def add_box(d_boxMargin, d_boxType='cubic'): utils.update("add_box", "adding box using gmx editconf (boxMargin = {0}, boxType = {1})...".format(d_boxMargin, d_boxType)) os.system("gmx editconf -f {0} -o {1}_BOX.pdb -d {2} -bt {3} >> builder.log 2>&1".format(universe.get('d_nameList')[-1], universe.get('d_pdbName'), d_boxMargin, d_boxType)) # To set d_boxMargin and d_boxType. universe.add('d_boxMargin', d_boxMargin) universe.add('d_boxType', d_boxType) # To update d_box. load("{0}_BOX.pdb".format(universe.get('d_pdbName'))) # To update d_nameList. utils.add_to_nameList("{0}_BOX.pdb".format(universe.get('d_pdbName')))
def energy_pcouple(): gen_mdp('NPT') utils.update("energy_pcouple", "running gmx grompp and mdrun for pressure coupling...") os.system( "gmx grompp -f NPT.mdp -c {0} -p topol.top -o NPT.tpr -r {0} >> builder.log 2>&1" .format(universe.get('d_nameList')[-1])) os.system( "gmx mdrun -deffnm NPT -c {0}_NPT.pdb >> builder.log 2>&1".format( universe.get('d_pdbName'))) utils.add_to_nameList("{0}_NPT.pdb".format(universe.get('d_pdbName')))
def fitCalibration(order=5, compare=[]): # Get relevant stuff from universe. # Note: these data-members are only created when calibrate.py is ran. dVdlInitList = universe.get('ph_dvdl_initList') dVdlMeanList = universe.get('ph_dvdl_meanList') dVdlStdList = universe.get('ph_dvdl_stdList') # Compute dV/dl coefficients. coeffs = np.polyfit(dVdlInitList, dVdlMeanList, order)[::-1] # Update user with the coefficients. print(coeffs) # Plot the computed values. plt.scatter(dVdlInitList, dVdlMeanList, label="mean dV/dl") plt.errorbar(dVdlInitList, dVdlMeanList, xerr=0, yerr=dVdlStdList, fmt='o', capsize=3, color='#1f77b4') # Our fit fit = [] for i in dVdlInitList: value = 0 for j in range(0, order + 1): value += coeffs[j] * i**j fit.append(value) plt.plot(dVdlInitList, fit, label="fit") # Comparison if len(compare) != 0: fit = [] for i in dVdlInitList: value = 0 for j in range(0, len(compare)): value += compare[j] * i**j fit.append(value) plt.plot(dVdlInitList, fit, label="compare") plt.title("Calibration for {}.pdb".format(universe.get('d_pdbName'))) plt.ylabel(r"dV/d$\lambda$") plt.xlabel(r"$\lambda$-coordinate") plt.legend() plt.grid() plt.show()
def countRes(resName): count = 0 for residue in universe.get('d_residues'): if (residue.d_resname == resName): count += 1 return count
def add_to_nameList(name): if universe.has('d_nameList'): temp = universe.get('d_nameList') temp.append(name) universe.add('d_nameList', temp) else: universe.add('d_nameList', [name])
def plotforces(pKa): R = 8.3145 * 10**-3 # "kJ * mol⁻1 * K^-1" T = 300 lambda_i = load.Col("lambda_dwp.dat", 1, 942, 2062) V_bias = load.Col("lambda_dwp.dat", 0, 942, 2062) pH = universe.get('ph_pH') V_pH = [] for i in lambda_i: V_pH.append(R * T * np.log(10) * (pKa - pH) * i) V_bias = np.gradient(V_bias) # Take derivatives. V_pH = np.gradient(V_pH) V_comb = [V_bias[i] + V_pH[i] for i in range(0, len(lambda_i))] plt.plot(lambda_i, V_bias, color="b", linestyle='--', label="$F_{bias}}$") plt.plot(lambda_i, V_pH, color="b", linestyle=':', label="$F_{pH}$") plt.plot(lambda_i, V_comb, color="b", label="$F_{combined}$") plt.ylim(-0.1, 0.1) plt.xlabel(r"$\lambda$-coordinate") plt.ylabel("Force") plt.grid() plt.legend() plt.show()
def plotlambda(plotBUF=False): resnameList = [] # Get the names and such of all the ASPs and GLUs. residList = [] for residue in universe.get('d_residues'): if residue.d_resname in ["ASP", "ASPH", "ASPT", "GLU", "GLUH", "GLUT"]: resnameList.append(residue.d_resname) residList.append(residue.d_resid) plt.figure() for idx in range(1, len(resnameList) + 1): t = load.Col("lambda_{0}.dat".format(idx), 1) x = load.Col("lambda_{0}.dat".format(idx), 2) plt.plot(t, x, label="%s-%s" % (resnameList[idx - 1], residList[idx - 1]), linewidth=0.5) if (plotBUF): t = load.Col("lambda_{0}.dat".format(len(resnameList) + 1), 1) x = load.Col("lambda_{0}.dat".format(len(resnameList) + 1), 2) plt.plot(t, x, label="Buffer", linewidth=0.5) plt.xlabel("Time (ps)") plt.ylabel(r"$\lambda$-coordinate") plt.ylim(-0.1, 1.1) plt.ticklabel_format(axis='x', style='sci', scilimits=(0, 3)) plt.legend() plt.grid() plt.show()
def plotpotentials(pKa): R = 8.3145 * 10**-3 # "kJ * mol⁻1 * K^-1" T = 300 lambda_i = load.Col("lambda_dwp.dat", 1, 942, 2062) V_bias = load.Col("lambda_dwp.dat", 0, 942, 2062) pH = universe.get('ph_pH') V_pH = [] for i in lambda_i: V_pH.append(R * T * np.log(10) * (pKa - pH) * i) V_comb = [] for i in range(0, len(lambda_i)): V_comb.append(V_bias[i] + V_pH[i]) plt.plot(lambda_i, V_bias, color="b", linestyle='--', label="$V_{bias}}$") plt.plot(lambda_i, V_pH, color="b", linestyle=':', label="$V_{pH}$") plt.plot(lambda_i, V_comb, color="b", label="$V_{combined}$") plt.xlabel(r"$\lambda$-coordinate") plt.ylabel(r"$V$ (kJ/mol)") plt.grid() plt.legend() plt.show()
def energy_tcouple(): gen_mdp('NVT') utils.update("energy_tcouple", "running gmx grompp and mdrun for temperature coupling...") os.system( "gmx grompp -f NVT.mdp -c {0} -p topol.top -o NVT.tpr -r {0} >> builder.log 2>&1" .format(universe.get('d_nameList')[-1])) os.system("gmx mdrun -deffnm NVT -c NVT.pdb >> builder.log 2>&1") utils.add_to_nameList("NVT.pdb")
def writeResBlock(number, name, indexLambda, indexName): addParam('lambda-dynamics-atom-set%s-name' % (number), name) addParam('lambda-dynamics-atom-set%s-lambda-residues-index' % (number), indexLambda) addParam('lambda-dynamics-atom-set%s-index-group-name' % (number), indexName) addParam('lambda-dynamics-atom-set%s-initial-lambda' % (number), lambdaInit) if universe.get('ph_restrainpH'): addParam( 'lambda-dynamics-atom-set%s-charge-restraint-group-index' % (number), 1) if (name == 'BUF'): addParam('lambda-dynamics-atom-set%s-buffer-residue' % (number), 'yes') addParam( 'lambda-dynamics-atom-set%s-buffer-residue-multiplier' % (number), universe.get('ph_bufnmol')) file.write('\n')
def rebuild_topol(): # If we have only one chain, gromacs will put everything in topol.top. # If we have more than one chain, gromacs will do it for us. if (len(universe.get('d_chain')) <= 1): readingProtein = False file = open("topol_Protein_chain_A.itp", 'w') for line in open("topol.top").readlines(): if (not readingProtein and line == "[ moleculetype ]\n"): readingProtein = True if (readingProtein and line == "; Include water topology\n"): readingProtein = False if (readingProtein): file.write(line) file.close() with open('topol.top', 'w') as file: file.write("; Include forcefield parameters\n") file.write("#include \"{0}.ff/forcefield.itp\"\n\n".format(universe.get('d_modelFF'))) file.write("; Include protein topology\n") for letter in universe.get('d_chain'): file.write("#include \"topol_Protein_chain_{0}.itp\"\n".format(letter)) file.write('\n') file.write('[ system ]\n') file.write('{0}\n\n'.format(universe.get('d_pdbName'))) file.write('[ molecules ]\n') file.write('; Compounts \t\t #mols\n') for letter in universe.get('d_chain'): file.write("Protein_chain_{0}\t\t1\n".format(letter))
def restrain_dihedrals_by_idx(indices, Type, phi, dphi, fc): utils.update("restrain_dihedrals", "will restrain dihedral {} {} {} {} (Type={}, phi={}, dphi={}, fc={})".format(indices[0], indices[1], indices[2], indices[3], Type, phi, dphi, fc)) # Every chain has its own .itp file, so we loop through every file: for letter in universe.get('d_chain'): # This is to make sure we don't have multiple headers when we add multiple different restraints. first = False if not "[ dihedral_restraints ]" in open("topol_Protein_chain_{0}.itp".format(letter)).read(): first = True # Append to the end of relevant .itp file: with open("topol_Protein_chain_{0}.itp".format(letter), 'a') as file: if first: file.write("[ dihedral_restraints ]\n") file.write("; ai aj ak al type phi dphi fc\n") for idx in indices: file.write("{:<6d} ".format(idx)) file.write(" {} {} {} {}\n".format(Type, phi, dphi, fc))
def add_buffer(ph_bufpdbName, ph_bufitpName, ph_bufMargin=2.0, ph_bufnmol=-1, attempts=100000): if not (universe.get('ph_constantpH') and universe.get('ph_restrainpH')): utils.update("add_buffer", "either ph_constantpH or ph_restrainpH is False --> skipping...") return # If user doesn't specified the amount, use #BUF = #ACID. if (ph_bufnmol == -1): ph_bufnmol = countRes('ASP') + countRes('GLU') utils.update("add_buffer", "will attempt to add {0} buffer molecule(s)...".format(ph_bufnmol)) # RUN GROMACS INSERT-MOLECULES COMMAND os.system("touch vdwradii.dat") # we need this dummy file for this to work. os.system("gmx insert-molecules -f {0} -o {1}_BUF.pdb -ci {2} -nmol {3} -scale 1.0 -radius {4} -try {5} >> builder.log 2>&1".format( universe.get('d_nameList')[-1], universe.get('d_pdbName'), ph_bufpdbName, ph_bufnmol, 0.5 * ph_bufMargin, int(attempts / ph_bufnmol))) os.remove("vdwradii.dat") # clean dummy file. # To update d_residues. load("{0}_BUF.pdb".format(universe.get('d_pdbName'))) # Give user a warning if there wasn't enough space. actual = countRes('BUF') if actual < ph_bufnmol: utils.update("add_buffer", "warning: only {0}/{1} requested buffer molecules inserted after {2} attempts,".format(actual, ph_bufnmol, attempts)) utils.update("add_buffer", "warning: try decreasing ph_bufMargin (={0}nm) or increasing d_boxMargin (={1}nm)...".format(ph_bufMargin, universe.get('d_boxMargin'))) else: utils.update("add_buffer", "succesfully added {0} buffer molecule(s)...".format(actual)) # To add buffer topology to topol.top. utils.update("add_buffer", "updating topology...") os.system("cp {} .".format(ph_bufitpName)) topol.add_mol(os.path.basename(ph_bufitpName), "Include buffer topology", 'BUF', actual) # Set some parameters in the universe. universe.add('ph_bufpdbName', ph_bufpdbName) universe.add('ph_bufitpName', ph_bufitpName) universe.add('ph_bufMargin', ph_bufMargin) universe.add('ph_bufnmol', actual) # To update d_nameList. utils.add_to_nameList("{0}_BUF.pdb".format(universe.get('d_pdbName')))
def gen_constantpH(ph_pH, ph_lambdaM, ph_nstout, ph_barrierE, cal=False, lambdaInit=0.5): # Skip this entire step if ph_constantpH is false. if (not universe.get('ph_constantpH')): utils.update("gen_constantpH", "ph_constantpH is False --> skipping...") return # Check whether MD.mdp exists and create if necessary. if (not os.path.isfile("MD.mdp")): utils.update("gen_constantpH", "MD.mdp does not exist, creating...") md.gen_mdp('MD', universe.get('d_nsteps'), universe.get('d_nstxout')) # Check whether index.ndx exists, and create if necessary. if (not os.path.isfile("index.ndx")): utils.update("gen_constantpH", "index.ndx does not exist, creating...") utils.generate_index() ############################################################################ file = open('MD.mdp', 'a') def addParam(name, value): # Formatting function. file.write("{:54s} = {:13s}\n".format(name, str(value))) # Update user. utils.update("gen_constantpH", "Writing general parameters:") utils.update("gen_constantpH", "ph_pH={}, ph_lambdaM={}, ph_nstout={}, ph_barrierE={}...".format(ph_pH, ph_lambdaM, ph_nstout, ph_barrierE)) # PART 1 - WRITE GENERAL PARAMETERS ######################################## file.write("\n; CONSTANT PH\n") addParam('lambda-dynamics', 'yes') addParam('lambda-dynamics-simulation-ph', ph_pH) addParam('lambda-dynamics-lambda-particle-mass', ph_lambdaM) addParam('lambda-dynamics-update-nst', ph_nstout) addParam('lambda-dynamics-tau', 2.0) # hardcoded if cal: # If we are in calibration mode: addParam('lambda-dynamics-calibration', 'yes') # If we use "charge-constraining" (2) scheme: if (universe.get('ph_QQleveling') == 2): addParam('lambda-dynamics-charge-constraints', 'yes') # Gather a list of the different lambda residue-type names that were # specified IN THE UNIVERSE by the user. lambdaTypeNamesSpecifed = [] for obj in universe.get('ph_lambdaTypes'): lambdaTypeNamesSpecifed.append(obj.d_resname) # Gather a list of the names of the lambda residues in the protein. lambdaResidueNameList = [] for residue in universe.get('d_residues'): if (residue.d_resname in lambdaTypeNamesSpecifed): lambdaResidueNameList.append(residue.d_resname) # Gather a list of the names of the lambda residue-types IN THE PROTEIN, # And make sure the order is the same as in ph_lambdaTypes = LambdaTypeNamesSpecified. lambdaResidueTypeList = [] for obj in universe.get('ph_lambdaTypes'): if (obj.d_resname in set(lambdaResidueNameList)): lambdaResidueTypeList.append(obj.d_resname) # If we use the charge leveling scheme "charge-restraining" (2) we also have # the BUF residue-type as well as one extra lambda group containing all the BUFs. if (universe.get('ph_QQleveling') == 2): addParam('lambda-dynamics-number-lambda-residues', len(lambdaResidueTypeList) + 1) addParam('lambda-dynamics-number-atom-collections', len(lambdaResidueNameList) + 1) else: addParam('lambda-dynamics-number-lambda-residues', len(lambdaResidueTypeList)) addParam('lambda-dynamics-number-atom-collections', len(lambdaResidueNameList)) file.write('\n') # PART 2 - WRITE RESIDUE-TYPE SPECIFIC STUFF ############################### def writeBlock(number, name, dvdl, pKa, ph_barrierE, qqA, qqB): def to_string(Input): string = "" for element in Input: string += "{:.3f} ".format(element) return string addParam('lambda-dynamics-residue%s-name' % (number), name) addParam('lambda-dynamics-residue%s-dvdl-coefficients' % (number), to_string(dvdl)) addParam('lambda-dynamics-residue%s-reference-pka' % (number), pKa) addParam('lambda-dynamics-residue%s-barrier' % (number), ph_barrierE) addParam('lambda-dynamics-residue%s-charges-state-A' % (number), to_string(qqA)) addParam('lambda-dynamics-residue%s-charges-state-B' % (number), to_string(qqB)) file.write('\n') # If we use a charge-leveling scheme, we need the buffer charge states. # These are added to universe when you run protein.add_buffer() if (universe.get('ph_QQleveling') in [1, 2]): BUF_qqA = universe.get('ph_bufqqA') # BUF_qqA = [-0.0656, 0.5328, 0.5328] previously hardcoded for water buffer BUF_qqB = universe.get('ph_bufqqB') # BUF_qqB = [-0.8476, 0.4238, 0.4238] previously hardcoded for water buffer idx = 1 for obj in universe.get('ph_lambdaTypes'): # This if-statement prevents writing a block when there are no residues of this type. if (obj.d_resname in lambdaResidueTypeList): # If we use "charge-coupling" (1) scheme, extend the charge states: if (universe.get('ph_QQleveling') == 1): writeBlock(idx, obj.d_resname, obj.d_dvdl[::-1], obj.d_pKa, ph_barrierE, obj.d_qqA + BUF_qqB, obj.d_qqB + BUF_qqA) else: writeBlock(idx, obj.d_resname, obj.d_dvdl[::-1], obj.d_pKa, ph_barrierE, obj.d_qqA, obj.d_qqB) idx += 1 if (universe.get('ph_QQleveling') == 2): # f we use "charge-constraining" (2) scheme, we additionaly need ph_BUF_dvdl. writeBlock(idx, 'BUF', universe.get('ph_BUF_dvdl')[::-1], 0, 0, BUF_qqA, BUF_qqB) # PART 3 - WRITE INDIVIDUAL RESIDUE/LAMBDA-GROUP STUF ###################### def writeResBlock(number, name, indexLambda, indexName): addParam('lambda-dynamics-atom-set%s-name' % (number), name) addParam('lambda-dynamics-atom-set%s-lambda-residues-index' % (number), indexLambda) addParam('lambda-dynamics-atom-set%s-index-group-name' % (number), indexName) addParam('lambda-dynamics-atom-set%s-initial-lambda' % (number), lambdaInit) if (universe.get('ph_QQleveling') == 2): addParam('lambda-dynamics-atom-set%s-charge-restraint-group-index' % (number), 1) if (name == 'BUF'): addParam('lambda-dynamics-atom-set%s-buffer-residue' % (number), 'yes') addParam('lambda-dynamics-atom-set%s-buffer-residue-multiplier' % (number), universe.get('ph_bufnmol')) file.write('\n') utils.update("gen_constantpH", "Writing individual lambda groups...") idx = 1 for name in lambdaResidueNameList: writeResBlock(idx, name, lambdaResidueTypeList.index(name) + 1, 'LAMBDA{}'.format(idx)) idx += 1 if (universe.get('ph_QQleveling') == 2): writeResBlock(idx, 'BUF', len(lambdaResidueTypeList) + 1, 'LAMBDA{}'.format(len(lambdaResidueNameList) + 1)) file.close() # MD.mdp # PART 4 - APPEND THE LAMBDA INDEX GROUPS TO INDEX.NDX ##################### utils.update("gen_constantpH", "Writing lambda index groups to index.ndx...") # If we use a charge-leveling scheme, we need a list of atomIndices of the BUFs: if (universe.get('ph_QQleveling') in [1, 2]): bufferAtomIndexList = [] count = 1 for residue in universe.get('d_residues'): for atom in residue.d_atoms: if (residue.d_resname == 'BUF'): bufferAtomIndexList.append(count) count += 1 file = open('index.ndx', 'a') # Append to existing index.ndx def writeTheGroup(number, atomIndexList): file.write('\n[ LAMBDA{} ]\n'.format(number)) for index in atomIndexList: file.write('{} '.format(index)) file.write('\n') ph_lambdaTypes = universe.get('ph_lambdaTypes') atomCount = 1; groupNumber = 1 for residue in universe.get('d_residues'): if residue.d_resname in lambdaResidueTypeList: atomIndexList = [] obj = ph_lambdaTypes[lambdaTypeNamesSpecifed.index(residue.d_resname)] for atom in residue.d_atoms: if atom in obj.d_atoms: atomIndexList.append(atomCount) atomCount += 1 # If we use "charge-coupling" (1), assign the atomIndices of one BUF # to one protonatable lambda residue (use clever list slicing): if (universe.get('ph_QQleveling') == 1): start = (groupNumber - 1) * len(BUF_qqA) stop = start + len(BUF_qqA) atomIndexList += bufferAtomIndexList[start:stop] writeTheGroup(groupNumber, atomIndexList) groupNumber += 1 else: for atom in residue.d_atoms: atomCount += 1 # If we use "charge-restraining" (2), add everything in bufferAtomIndexList # to the last lambda index group: if (universe.get('ph_QQleveling') == 2): writeTheGroup(groupNumber, bufferAtomIndexList) file.close() # index.ndx # Put relevant pH variables in universe universe.add('ph_pH', ph_pH) universe.add('ph_lambdaM', ph_lambdaM) universe.add('ph_nstout', ph_nstout) universe.add('ph_barrierE', ph_barrierE)
def gen_constantpH(ph_pH, ph_lambdaM, ph_nstout, ph_barrierE, cal=False, lambdaInit=0.5): # Hardcoded stuff GLU_pKa = 4.25 GLU_atoms = [' CG ', ' CD ', ' OE1', ' OE2', ' HE2'] # atoms part of model GLU_qqA = [-0.21, 0.75, -0.55, -0.61, 0.44] # protonated charge GLU_qqB = [-0.28, 0.62, -0.76, -0.76, 0.00] # deprotonated charge ASP_pKa = 3.65 ASP_atoms = [' CB ', ' CG ', ' OD1', ' OD2', ' HD2'] # atoms part of model ASP_qqA = [-0.21, 0.75, -0.55, -0.61, 0.44] # protonated charge ASP_qqB = [-0.28, 0.62, -0.76, -0.76, 0.00] # deprotonated charge BUF_qqA = [-0.0656, 0.5328, 0.5328] BUF_qqB = [-0.8476, 0.4238, 0.4238] # Skip this entire step if ph_constantpH is false. if (not universe.get('ph_constantpH')): utils.update("gen_constantpH", "ph_constantpH is False --> skipping...") return # Load dV/dl coefficients GLU_dvdl = universe.get('ph_GLU_dvdl') ASP_dvdl = universe.get('ph_ASP_dvdl') if (universe.get('ph_restrainpH')): BUF_dvdl = universe.get('ph_BUF_dvdl') # Check whether MD.mdp exists. if (not os.path.isfile("MD.mdp")): utils.update("gen_constantpH", "MD.mdp does not exist, creating...") md.gen_mdp('MD', universe.get('d_nsteps'), universe.get('d_nstxout')) # Check whether index.ndx exists. if (not os.path.isfile("index.ndx")): utils.update("gen_constantpH", "index.ndx does not exist, creating...") utils.generate_index() file = open('MD.mdp', 'a') # Formatting function. def addParam(name, value, comment="NUL"): if (comment == "NUL"): file.write("{:54s} = {:13s}\n".format(name, str(value))) else: file.write("{:54s} = {:13s} ; {:13s}\n".format( name, str(value), comment)) file.write("\n; CONSTANT PH\n") # PART 1 - WRITE GENERAL PARAMETERS ######################################## # Update user. utils.update("gen_constantpH", "Writing general parameters:") utils.update( "gen_constantpH", "ph_pH={}, ph_lambdaM={}, ph_nstout={}, ph_barrierE={}...".format( ph_pH, ph_lambdaM, ph_nstout, ph_barrierE)) addParam('lambda-dynamics', 'yes') addParam('lambda-dynamics-simulation-ph', ph_pH) addParam('lambda-dynamics-lambda-particle-mass', ph_lambdaM) addParam('lambda-dynamics-update-nst', ph_nstout) addParam('lambda-dynamics-tau', 2.0) # hardcoded if cal: addParam('lambda-dynamics-calibration', 'yes') if universe.get('ph_restrainpH'): addParam('lambda-dynamics-charge-constraints', 'yes') # Compile a list of acidic residues and their ResIDs. acidicResidueNameList = [] acidicResidueNumberList = [] acidicResidueTypeList = [] for residue in universe.get('d_residues'): if (residue.d_resname == 'GLU'): acidicResidueNameList.append('GLU') acidicResidueNumberList.append(residue.d_resid) if (residue.d_resname == 'ASP'): acidicResidueNameList.append('ASP') acidicResidueNumberList.append(residue.d_resid) if ('GLU' in acidicResidueNameList): acidicResidueTypeList.append('GLU') if ('ASP' in acidicResidueNameList): acidicResidueTypeList.append('ASP') if universe.get('ph_restrainpH'): # If we restrain the charge acidicResidueTypeList.append('BUF') # we also have BUF. addParam('lambda-dynamics-number-lambda-residues', len(acidicResidueTypeList)) if universe.get('ph_restrainpH'): addParam('lambda-dynamics-number-atom-collections', len(acidicResidueNameList) + 1) else: addParam('lambda-dynamics-number-atom-collections', len(acidicResidueNameList)) file.write('\n') # print(acidicResidueNameList) # debug # print(acidicResidueNumberList) # debug # print(acidicResidueTypeList) # debug # PART 2 - WRITE RESIDUE-TYPE SPECIFIC STUFF ############################### utils.update("gen_constantpH", "Writing residue-type specific stuff...") def writeBlock(number, name, dvdl, pKa, ph_barrierE, qqA, qqB): def to_string(Input): string = "" for element in Input: string += "{:.3f}".format(element) string += ' ' return string addParam('lambda-dynamics-residue%s-name' % (number), name) addParam('lambda-dynamics-residue%s-dvdl-coefficients' % (number), to_string(dvdl)) addParam('lambda-dynamics-residue%s-reference-pka' % (number), pKa) addParam('lambda-dynamics-residue%s-barrier' % (number), ph_barrierE) addParam('lambda-dynamics-residue%s-charges-state-A' % (number), to_string(qqA)) addParam('lambda-dynamics-residue%s-charges-state-B' % (number), to_string(qqB)) file.write('\n') for idx in range(0, len(acidicResidueTypeList)): if (acidicResidueTypeList[idx] == 'GLU'): writeBlock(idx + 1, 'GLU', GLU_dvdl, GLU_pKa, ph_barrierE, GLU_qqA, GLU_qqB) if (acidicResidueTypeList[idx] == 'ASP'): writeBlock(idx + 1, 'ASP', ASP_dvdl, ASP_pKa, ph_barrierE, ASP_qqA, ASP_qqB) if (acidicResidueTypeList[idx] == 'BUF'): # If number of protonatable residues != number of buffer molecules, # we need to increase buffer charge in state A by the ratio: nLams = protein.countRes('ASP') + protein.countRes('GLU') nBufs = universe.get('ph_bufnmol') BUF_qqA = [(nLams / float(nBufs)) * i for i in BUF_qqA] writeBlock(idx + 1, 'BUF', BUF_dvdl, 0, 0, BUF_qqA, BUF_qqB) # PART 3 - WRITE INDIVIDUAL RESIDUE/LAMBDA-GROUP STUF ###################### utils.update("gen_constantpH", "Writing individual lambda groups...") def writeResBlock(number, name, indexLambda, indexName): addParam('lambda-dynamics-atom-set%s-name' % (number), name) addParam('lambda-dynamics-atom-set%s-lambda-residues-index' % (number), indexLambda) addParam('lambda-dynamics-atom-set%s-index-group-name' % (number), indexName) addParam('lambda-dynamics-atom-set%s-initial-lambda' % (number), lambdaInit) if universe.get('ph_restrainpH'): addParam( 'lambda-dynamics-atom-set%s-charge-restraint-group-index' % (number), 1) if (name == 'BUF'): addParam('lambda-dynamics-atom-set%s-buffer-residue' % (number), 'yes') addParam( 'lambda-dynamics-atom-set%s-buffer-residue-multiplier' % (number), universe.get('ph_bufnmol')) file.write('\n') for idx in range(0, len(acidicResidueNameList)): writeResBlock( idx + 1, acidicResidueNameList[idx], acidicResidueTypeList.index(acidicResidueNameList[idx]) + 1, 'LAMBDA%s' % (idx + 1)) if universe.get('ph_restrainpH'): writeResBlock( len(acidicResidueNameList) + 1, 'BUF', acidicResidueTypeList.index('BUF') + 1, 'LAMBDA%s' % (len(acidicResidueNameList) + 1)) file.close() # MD.mdp # PART 4 - APPEND THE LAMBDA INDEX GROUPS TO INDEX.NDX ##################### utils.update("gen_constantpH", "Writing lambda index groups to index.ndx...") file = open('index.ndx', 'a') # Append to existing index.ndx # Function for adding an indexList to index.ndx def writeTheGroup(number, indexList): file.write('\n[ LAMBDA{} ]\n'.format(number)) for index in indexList: file.write('{} '.format(index)) file.write('\n') atomCount = 1 # Keeps track of the atom number. grpNum = 1 # Keeps track of the group (the LAMBDA%s). for residue in universe.get('d_residues'): # loop through all residues indexList = [] # clear indexList for atom in residue.d_atoms: # for each residue, loop through the atoms if (residue.d_resname == 'GLU' and atom in GLU_atoms): indexList.append(atomCount) elif (residue.d_resname == 'ASP' and atom in ASP_atoms): indexList.append(atomCount) atomCount += 1 # increment atomcount if (len(indexList) > 0): writeTheGroup(grpNum, indexList) grpNum += 1 if universe.get('ph_restrainpH'): atomCount = 1 indexList = [] for residue in universe.get('d_residues'): for atom in residue.d_atoms: if (residue.d_resname == 'BUF'): indexList.append(atomCount) atomCount += 1 writeTheGroup(grpNum, indexList) file.close() # index.ndx # Put relevant pH variables in universe universe.add('ph_pH', ph_pH) universe.add('ph_lambdaM', ph_lambdaM) universe.add('ph_nstout', ph_nstout) universe.add('ph_barrierE', ph_barrierE)
def glicphstates(): # EXPERIMENTAL DATA ON PROTONATION STATES AT VARIOUS PH #################### biophys = { # also prevost2012 'ASP-13': 1, 'ASP-31': 1, 'ASP-32': 1, 'ASP-49': 1, 'ASP-55': 1, 'ASP-86': 0, 'ASP-88': 0, 'ASP-91': 1, 'ASP-97': 1, 'ASP-115': 1, 'ASP-122': 1, 'ASP-136': 1, 'ASP-145': 1, 'ASP-153': 1, 'ASP-154': 1, 'ASP-161': 1, 'ASP-178': 1, 'ASP-185': 1, 'GLU-14': 1, 'GLU-26': 0, 'GLU-35': 0, 'GLU-67': 0, 'GLU-69': 1, 'GLU-75': 0, 'GLU-82': 0, 'GLU-104': 1, 'GLU-147': 1, 'GLU-163': 1, 'GLU-177': 0, 'GLU-181': 1, 'GLU-222': 1, 'GLU-243': 0, 'GLU-272': 1, 'GLU-282': 1 } nury2010 = { # this is also cheng2010, calimet2013 'ASP-13': 1, 'ASP-31': 1, 'ASP-32': 1, 'ASP-49': 1, 'ASP-55': 1, 'ASP-86': 0, 'ASP-88': 0, 'ASP-91': 1, 'ASP-97': 1, 'ASP-115': 1, 'ASP-122': 1, 'ASP-136': 1, 'ASP-145': 1, 'ASP-153': 1, 'ASP-154': 1, 'ASP-161': 1, 'ASP-178': 1, 'ASP-185': 1, 'GLU-14': 1, 'GLU-26': 0, 'GLU-35': 0, 'GLU-67': 0, 'GLU-69': 0, 'GLU-75': 0, 'GLU-82': 0, 'GLU-104': 1, 'GLU-147': 1, 'GLU-163': 1, 'GLU-177': 0, 'GLU-181': 1, 'GLU-222': 1, 'GLU-243': 0, 'GLU-272': 1, 'GLU-282': 1 } fritsch2011 = { 'ASP-13': 0, 'ASP-31': 0, 'ASP-32': 1, 'ASP-49': 1, 'ASP-55': 0, 'ASP-86': 0, 'ASP-88': 0, 'ASP-91': 0, 'ASP-97': 0, 'ASP-115': 1, 'ASP-122': 1, 'ASP-136': 1, 'ASP-145': 0, 'ASP-153': 0, 'ASP-154': 0, 'ASP-161': 0, 'ASP-178': 0, 'ASP-185': 0, 'GLU-14': 0, 'GLU-26': 0, 'GLU-35': 0, 'GLU-67': 0, 'GLU-69': 0, 'GLU-75': 0, 'GLU-82': 0, 'GLU-104': 1, 'GLU-147': 0, 'GLU-163': 0, 'GLU-177': 0, 'GLU-181': 0, 'GLU-222': 1, 'GLU-243': 0, 'GLU-272': 0, 'GLU-282': 0 } lev2017 = { 'ASP-13': 1, 'ASP-31': 1, 'ASP-32': 1, 'ASP-49': 1, 'ASP-55': 1, 'ASP-86': 1, 'ASP-88': 1, 'ASP-91': 1, 'ASP-97': 1, 'ASP-115': 1, 'ASP-122': 1, 'ASP-136': 1, 'ASP-145': 1, 'ASP-153': 1, 'ASP-154': 1, 'ASP-161': 1, 'ASP-178': 1, 'ASP-185': 1, 'GLU-14': 1, 'GLU-26': 0, 'GLU-35': 0, 'GLU-67': 0, 'GLU-69': 0, 'GLU-75': 0, 'GLU-82': 0, 'GLU-104': 1, 'GLU-147': 1, 'GLU-163': 1, 'GLU-177': 0, 'GLU-181': 1, 'GLU-222': 1, 'GLU-243': 0, 'GLU-272': 1, 'GLU-282': 1 } nemecz2017 = { # also Hu2018 'ASP-13': 1, 'ASP-31': 1, 'ASP-32': 1, 'ASP-49': 1, 'ASP-55': 1, 'ASP-86': 0, 'ASP-88': 0, 'ASP-91': 1, 'ASP-97': 1, 'ASP-115': 1, 'ASP-122': 1, 'ASP-136': 1, 'ASP-145': 1, 'ASP-153': 1, 'ASP-154': 1, 'ASP-161': 1, 'ASP-178': 1, 'ASP-185': 1, 'GLU-14': 1, 'GLU-26': 0, 'GLU-35': 0, 'GLU-67': 1, 'GLU-69': 1, 'GLU-75': 1, 'GLU-82': 1, 'GLU-104': 1, 'GLU-147': 1, 'GLU-163': 1, 'GLU-177': 1, 'GLU-181': 1, 'GLU-222': 0, 'GLU-243': 0, 'GLU-272': 1, 'GLU-282': 1 } ullman = { # unpublished 'ASP-13': 1, 'ASP-31': 1, 'ASP-32': 1, 'ASP-49': 1, 'ASP-55': 1, 'ASP-86': 1, 'ASP-88': 1, 'ASP-91': 1, 'ASP-97': 1, 'ASP-115': 1, 'ASP-122': 1, 'ASP-136': 1, 'ASP-145': 1, 'ASP-153': 1, 'ASP-154': 1, 'ASP-161': 1, 'ASP-178': 1, 'ASP-185': 1, 'GLU-14': 1, 'GLU-26': 0, 'GLU-35': 0, 'GLU-67': 0, 'GLU-69': 0, 'GLU-75': 0, 'GLU-82': 1, 'GLU-104': 1, 'GLU-147': 0, 'GLU-163': 0, 'GLU-177': 0, 'GLU-181': 1, 'GLU-222': 1, 'GLU-243': 0, 'GLU-272': 0, 'GLU-282': 1 } # DIRECTORY STRUCTURE ###################################################### dirname = "lambdaplots" if not os.path.isdir(dirname): os.mkdir(dirname) else: os.system("rm -f {0}/*.png {0}/*.pdf".format(dirname)) # GET THE RESIDUE NUMBER, NAME, AND CHAIN OF ALL PROTO RESIDUES ############ resnameList = [] residList = [] chainList = [] for residue in universe.get('d_residues'): if residue.d_resname in ["ASP", "GLU"]: resnameList.append(residue.d_resname) residList.append(residue.d_resid) chainList.append(residue.d_chain) # CREATE LAMBDA PLOT FOR EVERY INDIVIDUAL PROTONATABLE RESIDUE ############# # Loop through all the lambdas: # for idx in range(1, len(resnameList) + 1): # plt.figure(figsize=(8, 6)) # # Update user # print("plotting {}/{}".format(idx, len(resnameList)), end='\r') # # Load columns from .dat files # t = load.Col("lambda_{0}.dat".format(idx), 1) # x = load.Col("lambda_{0}.dat".format(idx), 2) # # Analyze a running sim not all columns will be equal long so trim: # if len(t) > len(x): # t.pop() # elif len(t) < len(x): # x.pop() # plt.plot(t, x, linewidth=0.5) # # Title # plt.title("{0}-{1} in chain {2} in {3}.pdb\npH={4}, nstlambda={5}, deprotonation={6:.2f}\n\ # Experimentally determined state for {0}-{1} at this pH = {7}".format( # resnameList[idx-1], # residList[idx-1], # chainList[idx-1], # universe.get('d_pdbName'), # universe.get('ph_pH'), # universe.get('ph_nstout'), # titrate("lambda_{}.dat".format(idx)), # expVals40["{0}-{1}".format(resnameList[idx-1], residList[idx-1])] # )) # # Axes and stuff # plt.ylim(-0.1, 1.1) # plt.xlabel("Time (ps)") # plt.ticklabel_format(axis='x', style='sci', scilimits=(0, 3)) # plt.ylabel(r"$\lambda$-coordinate") # plt.grid() # # Save. # fileName = "{}/{}_{}-{:03d}".format(dirname, chainList[idx-1], resnameList[idx-1], residList[idx-1]) # # plt.savefig("{}.pdf".format(fileName)); os.system("pdfcrop {0}.pdf {0}.pdf >> /dev/null 2>&1".format(fileName)) # plt.savefig("{}.png".format(fileName)) # # clf = clear the entire current figure. close = closes a window. # plt.clf(); plt.close() # CREATE HISTOGRAM PLOTS FOR COMBINED PROTO STATE OF ALL FIVE CHAINS ####### number_of_chains = len(set(chainList)) residues_per_chain = int(len(resnameList) / number_of_chains) for ii in range(1, residues_per_chain + 1): data = [] for jj in range(0, number_of_chains): print(ii + residues_per_chain * jj, end=' ') data += (load.Col( 'lambda_{}.dat'.format(ii + residues_per_chain * jj), 2, 49713, 124320)) print() # PLOTTING STUFF ####################################################### plt.figure(figsize=(8, 6)) plt.hist(data, density=True, bins=200) # Title plt.title( "{0}-{1} (all chains) in {2}.pdb\npH={3}, nstlambda={4}, deprotonation={5:.2f}" .format( resnameList[ii - 1], residList[ii - 1], universe.get('d_pdbName'), universe.get('ph_pH'), universe.get('ph_nstout'), titrate("lambda_{}.dat".format(ii)) # expVals40["{0}-{1}".format(resnameList[ii-1], residList[ii-1])] )) # Axes and stuff plt.axis([-0.1, 1.1, -0.1, 12]) plt.xlabel(r"$\lambda$-coordinate") plt.ticklabel_format(axis='x', style='sci', scilimits=(0, 3)) plt.grid() # Add green vertical line indicating experimental value plt.vlines(x=biophys["{0}-{1}".format(resnameList[ii - 1], residList[ii - 1])], ymin=0, ymax=12, color='r', linewidth=4.0, label="biophysics.se/Prevost2012 = {}".format( biophys["{0}-{1}".format(resnameList[ii - 1], residList[ii - 1])])) plt.vlines(x=nury2010["{0}-{1}".format(resnameList[ii - 1], residList[ii - 1])], ymin=0, ymax=10, color='g', linewidth=4.0, label="Nury2010/Cheng2010/Calimet2013 = {}".format( nury2010["{0}-{1}".format(resnameList[ii - 1], residList[ii - 1])])) plt.vlines(x=fritsch2011["{0}-{1}".format(resnameList[ii - 1], residList[ii - 1])], ymin=0, ymax=8, color='b', linewidth=4.0, label="Fritsch2011 = {}".format( fritsch2011["{0}-{1}".format(resnameList[ii - 1], residList[ii - 1])])) plt.vlines(x=lev2017["{0}-{1}".format(resnameList[ii - 1], residList[ii - 1])], ymin=0, ymax=6, color='c', linewidth=4.0, label="Lev2017 = {}".format(lev2017["{0}-{1}".format( resnameList[ii - 1], residList[ii - 1])])) plt.vlines(x=nemecz2017["{0}-{1}".format(resnameList[ii - 1], residList[ii - 1])], ymin=0, ymax=4, color='m', linewidth=4.0, label="Nemecz2017/Hu2018 = {}".format( nemecz2017["{0}-{1}".format(resnameList[ii - 1], residList[ii - 1])])) plt.vlines(x=ullman["{0}-{1}".format(resnameList[ii - 1], residList[ii - 1])], ymin=0, ymax=2, color='y', linewidth=4.0, label="Ullman (unpublished) = {}".format( ullman["{0}-{1}".format(resnameList[ii - 1], residList[ii - 1])])) plt.legend() # Save and clear fileName = "{}/hist_{}-{:03d}".format(dirname, resnameList[ii - 1], residList[ii - 1]) # plt.savefig("{}.pdf".format(fileName)); os.system("pdfcrop {0}.pdf {0}.pdf >> /dev/null 2>&1".format(fileName)) plt.savefig('{}.png'.format(fileName)) plt.clf() plt.close()
def add_buffer(ph_bufpdbName="", ph_bufitpName="", ph_bufqqA=[1], ph_bufqqB=[0], ph_bufMargin=2.5, attempts=100000): # This function writes a dummy .pdb containing the default buffer (ion). def writeDefaultPDB(): with open("defaultBuffer.pdb", 'w') as file: file.write("TITLE BUFFER PARTICLE\n") file.write("MODEL 1\n") file.write( "ATOM 1 NA BUF 1 110.896 2.872 68.855 1.00 0.00 \n" ) file.write("TER\n") file.write("ENDMDL\n") # This function writes the topology for the default buffer (ion). # Note: charge should not be 0 because then some interactions are not generated??? # Update: this was fixed by Berk in e2c2340. def writeDefaultITP(): with open("defaultBuffer.itp", 'w') as file: file.write("[ moleculetype ]\n") file.write("; molname nrexcl\n") file.write("BUF 1\n\n") file.write("[ atoms ]\n") file.write( "; id at type res nr residu name at name cg nr charge \n") file.write("1 SOD 1 BUF NA 1 0 \n\n") file.write("#ifdef POSRES_BUF\n") file.write("; Position restraint for each buffer ion\n") file.write("[ position_restraints ]\n") file.write("; i funct fcx fcy fcz\n") file.write(" 1 1 1000 1000 1000\n") file.write("#endif\n") # Skip this whole step if we don't need it. if not (universe.get('ph_constantpH') and universe.get('ph_QQleveling') in [1, 2]): utils.update( "add_buffer", "either ph_constantpH is False or ph_QQleveling = 0 --> skipping..." ) return # Make sure that the sum of the charges in state A and B are correct. if (sum(ph_bufqqA) != 1 or sum(ph_bufqqB) != 0): utils.warning( "add_buffer", "buffer charges incorrectly specified! sums must be 1 and 0") universe.get('ph_bufqqA') universe.get('ph_bufqqB') # Determine whether we use the default or a custom buffer. useDefault = False if (ph_bufpdbName == "" and ph_bufitpName == ""): utils.update("add_buffer", "using default (built-in) buffer...") useDefault = True elif (ph_bufpdbName == "" and ph_bufitpName != ""): utils.warning( "add_buffer", "ph_bufitpName not specified, resorting to default buffer!") useDefault = True elif (ph_bufpdbName != "" and ph_bufitpName == ""): utils.warning( "add_buffer", "ph_bufpdbName not specified, resorting to default buffer!") useDefault = True else: utils.update("add_buffer", "using custom buffer...") if (useDefault): # Check to make sure that the charges for the default buffer are correct. if (ph_bufqqA != [1] or ph_bufqqB != [0]): utils.warning( "add_buffer", "buffer charges incorrectly specified for default buffer!") universe.get('ph_bufqqA') universe.get('ph_bufqqB') # Generate the files for the default buffer and update data members. writeDefaultPDB() ph_bufpdbName = "defaultBuffer.pdb" writeDefaultITP() ph_bufitpName = "defaultBuffer.itp" # Get the number of buffer molecules we need. ph_bufnmol = 0 for lambdaType in universe.get('ph_lambdaTypes'): ph_bufnmol += countRes(lambdaType.d_resname) utils.update( "add_buffer", "will attempt to add {0} buffer molecule(s)...".format(ph_bufnmol)) # RUN GROMACS INSERT-MOLECULES COMMAND os.system( "touch vdwradii.dat") # we need this dummy file for this to work. os.system( "gmx insert-molecules -f {0} -o {1}_BUF.pdb -ci {2} -nmol {3} -scale 1.0 -radius {4} -try {5} >> builder.log 2>&1" .format( universe.get('d_nameList')[-1], universe.get('d_pdbName'), ph_bufpdbName, ph_bufnmol, 0.5 * ph_bufMargin, int(attempts / ph_bufnmol))) os.remove("vdwradii.dat") # clean dummy file. # To update d_residues. load("{0}_BUF.pdb".format(universe.get('d_pdbName'))) # Give user a warning if there wasn't enough space. actual = countRes('BUF') if actual < ph_bufnmol: utils.warning( "add_buffer", "only {0}/{1} requested buffer molecules inserted after {2} attempts," .format(actual, ph_bufnmol, attempts)) utils.warning( "add_buffer", "try decreasing ph_bufMargin (={0}nm) or increasing d_boxMargin (={1}nm)..." .format(ph_bufMargin, universe.get('d_boxMargin'))) else: utils.update( "add_buffer", "succesfully added {0} buffer molecule(s)...".format(actual)) # To add buffer topology to topol.top. utils.update("add_buffer", "updating topology...") if (useDefault): os.remove("defaultBuffer.pdb") # Remove dummy .pdb file. else: os.system("cp {} .".format(ph_bufitpName)) # Copy to working dir. topol.add_mol(os.path.basename(ph_bufitpName), "Include buffer topology", 'BUF', actual) # Set some parameters in the universe. universe.add('ph_bufpdbName', ph_bufpdbName) universe.add('ph_bufitpName', ph_bufitpName) universe.add('ph_bufqqA', ph_bufqqA) universe.add('ph_bufqqB', ph_bufqqB) universe.add('ph_bufMargin', ph_bufMargin) universe.add('ph_bufnmol', actual) # To update d_nameList. utils.add_to_nameList("{0}_BUF.pdb".format(universe.get('d_pdbName')))
def gen_mdp(Type, nsteps=25000, nstxout=0, posres=False): # HEAD if (Type not in ['EM', 'NVT', 'NPT', 'MD']): raise Exception( "Unknown .mdp Type specified. Types are: EM, NVT, NPT, MD.") utils.update( "gen_mdp", "Type={0}, nsteps={1}, nstxout={2}, posres={3}".format( Type, nsteps, nstxout, posres)) file = open("{0}.mdp".format(Type), 'w') def addTitle(title): file.write("\n; {0}\n".format(title.upper())) def addParam(name, value, comment=''): if (comment == ''): file.write("{:20s} = {:13s}\n".format(name, str(value))) else: file.write("{:20s} = {:13s} ; {:13s}\n".format( name, str(value), comment)) # POSITION RESTRAIN SECTION if Type in ['EM', 'MD']: if universe.get('ph_constantpH') and universe.get( 'ph_restrainpH') and posres: addTitle('Position restrain') addParam('define', '-DPOSRES -DPOSRES_BUF', 'Position restraints.') elif universe.get('ph_constantpH') and universe.get('ph_restrainpH'): addTitle('Position restrain') addParam('define', '-DPOSRES_BUF', 'Position restraints.') if (Type in ['NVT', 'NPT']): # position restrain temp and press coupling. addTitle('Position restrain') if universe.get('ph_constantpH') and universe.get('ph_restrainpH'): addParam('define', '-DPOSRES -DPOSRES_BUF', 'Position restraints.') else: addParam('define', '-DPOSRES', 'Position restraints.') # RUN CONTROL addTitle("Run control") if (Type == 'EM'): # emtol hardcored, pretty typical for normal MD. dt = 0.01 addParam('integrator', 'steep', 'Use steep for EM.') addParam('emtol', 1000, 'Stop when max force < 1000 kJ/mol/nm.') addParam('emstep', dt, 'Time step (ps).') if (Type in ['NVT', 'NPT', 'MD']): dt = 0.002 addParam('integrator', 'md') addParam('dt', dt, 'Time step (ps).') addParam('nsteps', nsteps, '%.1f ns.' % ((dt * nsteps) / 1000.0)) # We restrain the COM to prevent protein from coming too close to the BUFs. if Type == 'MD' and universe.get('ph_restrainpH'): addParam('comm-mode', 'Linear', 'Remove center of mass translation.') addParam('comm-grps', 'Protein Non-Protein') # OUTPUT CONTROL addTitle("Output control") addParam('nstxout-compressed', nstxout, 'Write frame every %.3f ps.' % (dt * nstxout)) # NEIGHBOUR SEARCHING PARAMETERS addTitle("Neighbour searching") addParam('cutoff-scheme', 'Verlet', 'Related params are inferred by Gromacs.') # BONDED if (Type in ['NVT', 'NPT', 'MD']): addTitle("Bond parameters") addParam('constraints', 'h-bonds', 'Constrain H-bond vibrations.') addParam('constraint_algorithm', 'lincs', 'Holonomic constraints.') addParam('lincs_iter', 1, 'Related to accuracy of LINCS.') addParam('lincs_order', 4, 'Related to accuracy of LINCS.') # ELECTROSTATICS addTitle("Electrostatics") addParam('coulombtype', 'PME', 'Use Particle Mesh Ewald.') if (universe.get('d_modelFF')[0:5].lower() == "charm" ): # if we use a CHARMM force field... addParam('rcoulomb', 1.2, 'Berk: CHARMM was calibrated for 1.2 nm.') addParam('fourierspacing', 0.14, 'Berk: set this to 0.14 for CHARMM.') else: # Default for force fields: addParam('rcoulomb', 1.0, 'Coulomb cut-off (nm).') # VAN DER WAALS addTitle("Van der Waals") addParam('vdwtype', 'cut-off', 'Twin range cut-off with nblist cut-off.') if (universe.get('d_modelFF')[0:5].lower() == "charm" ): # if we use a CHARMM force field... addParam('rvdw', 1.2, 'Berk: CHARMM was calibrated for 1.2 nm.') addParam('vdw-modifier', 'force-switch', 'Berk: specific for CHARMM.') addParam('rvdw-switch', 1.0, 'Berk: specific for CHARMM.') else: # Default for force fields: addParam('rvdw', 1.0, 'Van der Waals cut-off (nm).') # TEMPERATURE COUPLING if (Type in ['NVT', 'NPT', 'MD']): addTitle("Temperature coupling") addParam('tcoupl', 'v-rescale') addParam('tc-grps', 'SYSTEM') addParam('tau-t', 0.5, 'Berk: change from 0.1 to 0.5.') addParam('ref-t', 300, 'Reference temp. (K) (for each group).') # PRESSURE COUPLING if (Type in ['NPT', 'MD']): addTitle('Pressure coupling') if (Type == 'NPT'): addParam('pcoupl', 'Berendsen', 'Use Berendsen for NPT.') else: addParam('pcoupl', 'Parrinello-Rahman') addParam('pcoupltype', 'isotropic', 'Uniform scaling of box.') addParam('tau_p', 5.0, 'Berk: better to change from 2.0 to 5.0.') addParam('ref_p', 1.0, 'Reference pressure (bar).') addParam('compressibility', 4.5e-5, 'Isothermal compressbility of water.') addParam('refcoord_scaling', 'all', 'Required with position restraints.') # PERIODIC BOUNDARY CONDITIONS addTitle("Periodic boundary condition") addParam('pbc', 'xyz', 'To keep molecule(s) in box.') # GENERATE VELOCITIES FOR STARTUP if (Type == 'NVT'): addTitle('Generate velocities for startup') addParam('gen_vel', 'yes') file.close() # PUT RELEVANT PARAMETERS IN UNIVERSE if (Type == 'MD'): universe.add('d_dt', dt) universe.add('d_nsteps', nsteps) universe.add('d_nstxout', nstxout)
def generate(d_modelFF, d_modelWater, d_terministring="", d_customitp=""): # Internal helper function. def rebuild_topol(): # If we have only one chain, gromacs will put everything in topol.top. # If we have more than one chain, gromacs will do it for us. if (len(universe.get('d_chain')) <= 1): readingProtein = False file = open("topol_Protein_chain_A.itp", 'w') for line in open("topol.top").readlines(): if (not readingProtein and line == "[ moleculetype ]\n"): readingProtein = True if (readingProtein and line == "; Include water topology\n"): readingProtein = False if (readingProtein): file.write(line) file.close() with open('topol.top', 'w') as file: file.write("; Include forcefield parameters\n") file.write("#include \"{0}.ff/forcefield.itp\"\n\n".format(universe.get('d_modelFF'))) file.write("; Include protein topology\n") for letter in universe.get('d_chain'): file.write("#include \"topol_Protein_chain_{0}.itp\"\n".format(letter)) file.write('\n') file.write('[ system ]\n') file.write('{0}\n\n'.format(universe.get('d_pdbName'))) file.write('[ molecules ]\n') file.write('; Compounts \t\t #mols\n') for letter in universe.get('d_chain'): file.write("Protein_chain_{0}\t\t1\n".format(letter)) # ADD RELEVANT PARAMETERS TO UNIVERSE ###################################### universe.add('d_modelFF', d_modelFF) universe.add('d_modelWater', d_modelWater) universe.add('d_terministring', d_terministring) # USER UPDATE STUFF ######################################################## countACID = protein.countRes("ASP") + protein.countRes("GLU") # If constant-pH is on, if universe.get('ph_constantpH'): utils.update("generate", 'constant-pH is turned on...') # and we have at least one protonatable reside, if countACID > 0: utils.update("generate", "detected {} acidic residue(s):".format(countACID)) for letter in universe.get('d_chain'): count = 1 for residue in universe.get('d_residues'): if residue.d_chain == letter: count += 1 if residue.d_resname in ['ASP', 'GLU']: utils.update("generate", "{:3s}-{:<4d} in chain {}".format(residue.d_resname, count, letter)) utils.update("generate", "(setting protonation state to true (option 1) for all of these)") else: utils.update("generate", "no acidic residues detected, constant-pH is turned off...") universe.add('ph_constantpH', False) universe.add('ph_restrainpH', False) else: utils.update("generate", 'constant-pH is turned off...') universe.add('ph_restrainpH', False) # If ph_constantpH is False then this is also False. utils.update("generate", "using the {} force field with the {} water model...".format(d_modelFF, d_modelWater)) # CREATE EOFSTRING FOR PDB2GMX COMMAND ##################################### # Here we create EOFstring to circumvent pd2gmx prompting for user input. xstr = "<< EOF" # The for-loop sets the protonation state of all GLUs and ASPs to 1 # (True) if ph_constantpH is True, and to 0 (False) if ph_constantpH is False. for chain in universe.get('d_chain'): for residue in universe.get('d_residues'): if residue.d_resname in ['ASP', 'GLU'] and residue.d_chain == chain: xstr += "\n{:d}".format(universe.get('ph_constantpH')) # Furthermore, if the user specified a two-letter string for the termini, # add those to the EOFstring as well: if (d_terministring != ""): xstr += "\n{}".format(d_terministring[0]) xstr += "\n{}".format(d_terministring[1]) # End EOFstring: xstr += "\nEOF" # print(xstr) # Debug # UPDATE USER ABOUT WHAT WE DO WITH THE TERMINI ############################ if (d_terministring != ""): utils.update("generate", "Using options {} for termini...".format(d_terministring)) else: utils.update("generate", "No termini specified, using gmx default (00 = NH3+ and COO-)...") utils.update("generate", "running pdb2gmx to create {}_PR2.pdb and topol.top...".format(universe.get('d_pdbName'))) # RUN ACTUAL PDB2GMX COMMAND ############################################### if (d_terministring != ""): os.system("gmx pdb2gmx -f {0} -o {1}_PR2.pdb -asp -glu -ignh -ff {2} -water {3} -ter >> builder.log 2>&1 {4}".format(universe.get('d_nameList')[-1], universe.get('d_pdbName'), d_modelFF, d_modelWater, xstr)) else: os.system("gmx pdb2gmx -f {0} -o {1}_PR2.pdb -asp -glu -ignh -ff {2} -water {3} >> builder.log 2>&1 {4}".format(universe.get('d_nameList')[-1], universe.get('d_pdbName'), d_modelFF, d_modelWater, xstr)) # Rebuild topology. rebuild_topol() # Use custom topol_Protein_chain_A.itp (this is a temporary fix for charmm36-mar2019-m4) if (d_customitp != ""): utils.update("generate", "Overwriting topol_Protein_chain_A.itp generated by pdb2gmx with {}...".format(d_customitp)) os.system("cp {} .".format(d_customitp)) # To update d_residues. protein.load("{0}_PR2.pdb".format(universe.get('d_pdbName'))) # To update d_nameList. utils.add_to_nameList("{0}_PR2.pdb".format(universe.get('d_pdbName')))
def generate_index(): os.system("gmx make_ndx -f {0} >> builder.log 2>&1 << EOF\nq\nEOF".format( universe.get('d_nameList')[-1]))