Code
distanceBand = 0
versionString = "1.1.1"
-- Hello! Welcome to the recipe code of DDG Calculatr 1.1.1! Feel free to read through the code. Please PM me if you have any questions or comments.
-- This recipe calculates the DDG Binding Energy of the complex of the locked and unlocked segments of the puzzle in kCal/mol (kilocalorie per mole) and e^2/Angstrom (elementary charge squared per angstrom). This recipe also calculates the three most attractive and repulsive amino acids in the locked and unlocked segments.
-- It is somewhat similar to and based on how DDG is formally calculated in Rosetta.
-- Instead of the various Rosetta subscores, this recipe takes into consideration the potential energy of the complex of the two segments as determined by the force between every pair of amino acids that have one on the unlocked segment and one on the locked segment of the puzzle as determined by Coulomb's inverse-square law.
-- But this time in version 1.1.1, I fixed a problem with the ranking of amino acid pairs, and I made the recipe more accurate by adding a band if the distance between the amino acids is below a threshold.
--
-- Thank you for reading through this message!
function hasNoChargedAtoms(AA)
return (AA == 'c') or (AA == 'u') or (AA == 'g') or (AA == 'p') or (AA == 'a') or (AA == 'v') or (AA == 'i') or (AA == 'l') or (AA == 'm') or (AA == 'f')
end
function hasAChargedAtom(AA)
return (AA == 'k') or (AA == 's') or (AA == 't') or (AA == 'y') or (AA == 'w')
end
function hasTwoChargedAtoms(AA)
return (AA == 'h') or (AA == 'd') or (AA == 'e') or (AA == 'n') or (AA == 'q')
end
function getBondableAtoms(AA)
if(hasAChargedAtom(AA)) then
if (AA == 'k') or (AA == 'w') then
--The amino acid has a donor atom
return "Donor"
else
--The amino acid has a purple atom
return "Donoracceptor"
end
else
if(hasTwoChargedAtoms(AA)) then
if (AA == 'd') or (AA == 'e') then
--The amino acid has two acceptor atoms
return "Diacceptor"
else
--The amino acid has a donor and acceptor atom
return "Donoracceptor"
end
end
end
end
function main()
print("Hello " .. user.GetPlayerName() .. "! Welcome to DDG Calculatr " .. versionString .. ", by donuts554!")
print("For reference, in the context of this recipe, kCal/mol = kilocalorie per mole, e = elementary charge, Ang. = Angstrom, e^2/Ang. = elementary charge squared per angstrom, seg. = segment, and AA = amino acid.")
if not (structure.IsLocked(1)) then
errorDialog = dialog.CreateDialog("Error")
errorDialog.Text = dialog.AddLabel("There are no locked segments in this puzzle.")
errorDialog.OKButton = dialog.AddButton("OK", 1)
dialog.Show(errorDialog)
return
end
lastUnlockedSeg = structure.GetCount()
if structure.IsLocked(lastUnlockedSeg) then
errorDialog = dialog.CreateDialog("Error")
errorDialog.Text = dialog.AddLabel("There are locked segments that have indexes after the unlocked segment indexes.")
errorDialog.OKButton = dialog.AddButton("OK", 1)
dialog.Show(errorDialog)
return
end
print("There are locked segments in this puzzle.")
print("Finding the index of the first unlocked segment...")
incrementMax = -9999999
incrementMax2 = -9999998
incrementMax3 = -9999997
incrementMin = 9999999
incrementMin2 = 9999998
incrementMin3 = 9999997
distMax = ""
distMax2 = ""
distMax3 = ""
distMin = ""
distMin2 = ""
distMin3 = ""
seg = 1
while structure.IsLocked(seg) do
seg = seg + 1
end
firstUnlockedSeg = seg
print("The index of the first unlocked segment has been found!")
--Amino acids with sulfur aren't polar in this case
print("Calculating DDG...")
lockedAA = ""
unlockedAA = ""
DDG = 0
-- firstUnlockedSeg-1 is the last locked segment
for i = 1, firstUnlockedSeg-1, 1 do
print("Seg. " .. i .. "/" .. firstUnlockedSeg-1 .. " of locked seg. - DDG (e^2/Ang.) so far: " .. DDG .. ".")
lockedAA = structure.GetAminoAcid(i)
--print(lockedAA)
if hasNoChargedAtoms(lockedAA) then
--print("Segment " .. i .. " has no charged atoms in its sidechain.")
else
for j = firstUnlockedSeg, lastUnlockedSeg, 1 do
--print(i)
--print("Calculating index " .. j-firstUnlockedSeg+1 .. "/" .. lastUnlockedSeg-firstUnlockedSeg+1 .. " of the unlocked segment - The DDG so far is " .. DDG*structure.GetDistance(i,j) .. ".")
unlockedAA = structure.GetAminoAcid(j)
if hasNoChargedAtoms(unlockedAA) then
--print("Segment " .. j .. " has no charged atoms in its sidechain.")
else
lockedBondables = getBondableAtoms(lockedAA)
unlockedBondables = getBondableAtoms(unlockedAA)
if(lockedBondables == "Donor") then
if(unlockedBondables == "Donor") then
updateDDG(1, i ,j)
else
if(unlockedBondables == "Donoracceptor") then
updateDDG(-1, i ,j)
else
if(unlockedBondables == "Diacceptor") then
updateDDG(-2, i ,j)
else
--The unlocked amino acid is Arginine
updateDDG(3, i ,j)
end
end
end
else
if(lockedBondables == "Donoracceptor") then
if(unlockedBondables == "Donor") then
updateDDG(-1, i ,j)
else
if(unlockedBondables == "Donoracceptor") then
updateDDG(-2, i ,j)
else
if(unlockedBondables == "Diacceptor") then
updateDDG(-2, i ,j)
else
--The unlocked amino acid is Arginine
updateDDG(-3, i ,j)
end
end
end
else
if(lockedBondables == "Diacceptor") then
if(unlockedBondables == "Donor") then
updateDDG(-2, i ,j)
else
if(unlockedBondables == "Donoracceptor") then
updateDDG(-2, i ,j)
else
if(unlockedBondables == "Diacceptor") then
updateDDG(4, i ,j)
else
--The unlocked amino acid is Arginine
updateDDG(-6, i ,j)
end
end
end
else
--The locked amino acid is Arginine
if(unlockedBondables == "Donor") then
updateDDG(3, i ,j)
else
if(unlockedBondables == "Donoracceptor") then
updateDDG(-3, i ,j)
else
if(unlockedBondables == "Diacceptor") then
updateDDG(-6, i ,j)
else
--The locked amino acid is Arginine
updateDDG(9, i, j)
end
end
end
end
end
end
end
end
end
end
--This is the net conversion based on the conversion from Angstrom to Meter, Coloumb's Law (in Joules), and the conversion from Joule to Calorie to Kilocalorie.
kCalDDG = DDG * 0.214807880367
print("DDG is calculated!")
print("DDG = " .. DDG .. " e^2/Angstrom, which is " .. kCalDDG .. " kCal/mol.")
print("Most repulsive AA pairs (" .. incrementMax * 0.214807880367 .." kCal/mol): " .. distMax .. ", " .. distMax2 .. ", & " .. distMax3)
print("Most attractive AA pairs (" .. incrementMin * 0.214807880367 .." kCal/mol): " .. distMin .. ", " .. distMin2 .. ", & " .. distMin3)
DDGDialog = dialog.CreateDialog("Calculation Results")
DDGDialog.TextEAngstrom = dialog.AddLabel("DDG = " .. DDG .. " e^2/Angstrom")
DDGDialog.TextKcalMole = dialog.AddLabel("Which is " .. kCalDDG .. " kCal/mol")
DDGDialog.TextRepulsiveHead = dialog.AddLabel("Repulsive AA pairs (" .. incrementMax .. " kCal/mol) ")
DDGDialog.TextRepulsiveList = dialog.AddLabel(distMax .. ", " .. distMax2 .. ", & " .. distMax3)
DDGDialog.TextAttractive = dialog.AddLabel("Attractive AA pairs (" .. incrementMin .. " kCal/mol) ")
DDGDialog.TextAttractiveList = dialog.AddLabel(distMin .. ", " .. distMin2 .. ", & " .. distMin3)
DDGDialog.OKButton = dialog.AddButton("OK", 1)
dialog.Show(DDGDialog)
end
function updateDDG(chargeProduct, i, j)
distance = structure.GetDistance(i, j)
-- 9.36 Angstroms is the distance between the endpoints of a straightforward zigzag 6 (not 7 because the hydrogen atom can be in a hydrogen bond [Hydrogen atom can be intersected in a hydrogen bond]) atoms long (lysine [pointing straight outwards] - alpha carbon to nitrogen), doubled.
-- Note: The path from the alpha carbon of Tryptophan to the farthest atom on the sidechain of that amino acid is six atoms long, but is shorter due to the path not being a straightforward zigzag.
-- The reason why this distance is chosen is because it is the closest distance where the length of the biggest sidechain, doubled, can fit in, and that if the distance is shorter, then the number of possible ways the sidechains on both sides can be in gets significantly more limited. This limitation characterizes the significance of the positions of the sidechains, and so the minimum distance where this limitation is unsignificant is used.
-- I would like to point out that I have considered the thought that two lysine sidechains pointing straight at each other with their alpha carbons 9.36 Angstroms apart would cause a clash, and so that this configuration would not be possible. However, this is only just one known impossible configuration, so the limitation is not significant. If the distance becomes more shorter, then the much larger number of configurations only possible with the alpha carbons 9.36 Angstroms apart would be impossible, and the muc larger number of impossible configurations is what makes the limitation significant.
if (distance < 9.36) then
if((lockedBondables == "Donor") or (lockedAA == 'r')) then
atomIndex1 = 9
else
if((lockedBondables == "Diacceptor") or (lockedAA == 'q')) then
atomIndex1 = 7
else
if ((lockedAA == 's') or (lockedAA == 't') or (lockedAA == 'n')) then
atomIndex1 = 6
else
if(lockedAA == 'h') then
atomIndex1 = 9
else
-- lockedAA = Tyrosine
atomIndex1 = 12
end
end
end
end
if((unlockedBondables == "Donor") or (unlockedAA == 'r')) then
atomIndex2 = 9
else
if((unlockedBondables == "Diacceptor") or (unlockedAA == 'q')) then
atomIndex2 = 7
else
if ((unlockedAA == 's') or (unlockedAA == 't') or (unlockedAA == 'n')) then
atomIndex2 = 6
else
if(unlockedAA == 'h') then
atomIndex2 = 9
else
-- lockedAA = Tyrosine
atomIndex2 = 12
end
end
end
end
distanceBand = band.AddBetweenSegments(i, j, atomIndex1, atomIndex2)
if(not(distanceBand == 0)) then
distance = band.GetLength(distanceBand)
band.Delete(distanceBand)
distanceBand = 0
end
end
increment = chargeProduct/distance
if(increment > incrementMax) then
distMax3 = distMax2
distMax2 = distMax
distMax = i .. "&" .. j
incrementMax = increment
else
if(increment > incrementMax2) then
distMax3 = distMax2
distMax2 = i .. "&" .. j
incrementMax2 = increment
else
if(increment > incrementMax3) then
distMax3 = i .. "&" .. j
incrementMax3 = increment
end
end
end
if(increment < incrementMin) then
distMin3 = distMin2
distMin2 = distMin
distMin = i .. "&" .. j
incrementMin = increment
else
if(increment < incrementMin2) then
distMin3 = distMin2
distMin2 = i .. "&" .. j
incrementMin2 = increment
else
if(increment < incrementMin3) then
distMin3 = i .. "&" .. j
incrementMin3 = increment
end
end
end
DDG = DDG + increment
end
function handleError(err)
if(not(distanceBand == 0)) then
band.Delete(distanceBand)
end
if string.find(err, "Cancelled")~=nil then
print("The recipe was cancelled.")
else
print("Sadly, there is an error.")
print(err)
end
end
status = xpcall(main, handleError)