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 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 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 EM.pdb >> builder.log 2>&1") utils.add_to_nameList("EM.pdb")
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 NPT.pdb >> builder.log 2>&1") utils.add_to_nameList("NPT.pdb")
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_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 {0}_NVT.pdb >> builder.log 2>&1".format( universe.get('d_pdbName'))) utils.add_to_nameList("{0}_NVT.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 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 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 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')))