Icon representing a recipe

Recipe: Missing Ligand 1.0.1 -- brow42

created by brow42

Profile


Name
Missing Ligand 1.0.1 -- brow42
ID
46378
Shared with
Public
Parent
None
Children
None
Created on
November 29, 2013 at 01:42 AM UTC
Updated on
November 29, 2013 at 01:42 AM UTC
Description

Add bands for missing constraints. Pick the bond shape for the missing atom and band a group of atoms in that shape. Distances set by atom type. Yes, I know it has trouble guessing the right group.

Best for


Code


--[[ * Missing Ligand * * Residues should be bonding to a ligand, which is missing * The ligand could be replaced by constraints * If constraints are missing, then make bands * * Version 0.1 June 28 2013 brow42 * Version 1.0 Nov 28 2013 brow42 * Added tetrahedral symmetry * Will now work witih O-terminal cysteine * Added Help --]] title = "Missing Ligand 1.0.1" -- ================================== Begin New Dialog Library Functions -- Add a wall of text from a table function dialog.AddLabels(d,msg,nlabels) -- pass in # of existing autolabels local nlabels = nlabels or #(d._Order or {}) -- default, valid if never delete dialog elements if type(msg) == 'string' then msg = { msg } end for i = 1,#msg do d['autolabel'..tostring(i+nlabels)] = dialog.AddLabel(msg[i]) end end -- Create but don't display a wall of text and 1 or 2 buttons function dialog.CreateMessageBox(msg,title,buttontext1,buttontext0) title = title or '' local d = dialog.CreateDialog(title) dialog.AddLabels(d,msg) buttontext1 = buttontext1 or 'Ok' d.button = dialog.AddButton(buttontext1,1) if buttontext0 ~= nil then d.button0 = dialog.AddButton(buttontext0,0) end return d end -- Display a dialog box function dialog.ShowMessageBox(msg,title,buttontext1,buttontext0) return dialog.Show(dialog.CreateMessageBox(msg,title,buttontext1,buttontext0)) end dialog.T_CHECKBOX = 1 function dialog.FirstCheckedBox(d) for u,v in pairs(d) do if v.controlType and v.controlType == dialog.T_CHECKBOX and v.value == true then return u end end end function dialog.CountCheckedBoxes(d) n = 0 for u,v in pairs(d) do if v.controlType and v.controlType == dialog.T_CHECKBOX and v.value == true then n = n + 1 end end return n end function dialog.ListCheckedBoxes(d) l = { } for u,v in pairs(d) do if v.controlType and v.controlType == dialog.T_CHECKBOX and v.value == true then l[#l+1] = u end end return l end -- ================================== End New Dialog Library Functions -- ================================== Begin Residue Grouping Functions function BuildCluster(aas) -- minimize perimeter, won't actually find optimum because it's order dependent -- find first two local cluster,score,n cluster = {} score = math.huge n = structure.GetCount() for i = 1, n-1 do for j = i+1,n do if structure.GetAminoAcid(i) == aas[1] and structure.GetAminoAcid(j) == aas[2] and structure.GetDistance(i,j) < score then cluster = { i, j} score = structure.GetDistance(i,j) end end end for k = 3,#aas do score = math.huge local index = 0 for i = 1,n do if structure.GetAminoAcid(i) == aas[k] then local tmp = 0 for j = 1,#cluster do if i == cluster[j] then tmp = math.huge break end tmp = tmp + structure.GetDistance(i,cluster[j]) end if tmp < score then score = tmp index = i end end -- if aa end -- score,index now have closest aa of the correct type if index == 0 then print ("No cluster") return {} end cluster[#cluster+1] = index end print("Cluster:") for i = 1,#cluster do print(i,cluster[i],structure.GetAminoAcid(cluster[i])) end return cluster end function BestTwoPair(cluster) -- can't be bothered to generalize this yet local best, pairs, len best = structure.GetDistance(cluster[1],cluster[2]) + structure.GetDistance(cluster[3],cluster[4]) len = best pairs = {{cluster[1],cluster[2]},{cluster[3],cluster[4]}} len = structure.GetDistance(cluster[1],cluster[3]) + structure.GetDistance(cluster[2],cluster[4]) if len < best then len = best pairs = {{cluster[1],cluster[3]},{cluster[2],cluster[4]}} end len = structure.GetDistance(cluster[1],cluster[4]) + structure.GetDistance(cluster[2],cluster[3]) if len < best then len = best pairs = {{cluster[1],cluster[4]},{cluster[2],cluster[3]}} end best = structure.GetDistance(pairs[1][1],pairs[2][1]) + structure.GetDistance(pairs[2][1],pairs[2][2]) len = structure.GetDistance(pairs[1][1],pairs[2][2]) + structure.GetDistance(pairs[2][1],pairs[2][1]) if len < best then pairs[2] = { pairs[2][2] , pairs[2][1] } end return pairs end -- ================================== End Residue Grouping Functions -- ================================== Begin Ligand Functions -- Return the atom number of the sulfur in a cysteine, or nil if not cys function GetSulfurAtom(iSeg) if structure.GetSecondaryStructure(iSeg) == 'M' then return nil end if structure.GetAminoAcid(iSeg) ~= 'c' then return nil end local diff = structure.GetAtomCount(iSeg) - 11 if current.GetSegmentEnergySubscore(iSeg,'disulfides') ~= tonumber('-0') then diff = diff + 1 end if diff == 1 or diff == 3 then return 7 end return 6 end function GetNitrogenAtom(iSeg) if structure.GetSecondaryStructure(iSeg) == 'M' then return nil end if structure.GetAminoAcid(iSeg) ~= 'h' then return nil end local diff = structure.GetAtomCount(iSeg) - 17 if diff == 1 or diff == 3 then return 11 end return 10 end function M2S2Cys4() print "Fe2S2Cys4 Ferredoxin-like" cluster = BuildCluster({'c','c','c','c'}) if #cluster == 0 then return end -- Find best pair closest cluster = BestTwoPair(cluster) d = dialog.CreateDialog('M2S2Cys4 Positioning') d.label = dialog.AddLabel("If these are the wrong pairs, re-arrange the AAs") d.pair1 = dialog.AddLabel(string.format('First Pair: %d %d',cluster[1][1],cluster[1][2])) d.pair2 = dialog.AddLabel(string.format('Second Pair: %d %d',cluster[2][1],cluster[2][2])) -- d.label2 = dialog.AddLabel("") d.Label3 = dialog.AddLabel("Pick Metal Atom (the distances)") d.Fe = dialog.AddCheckbox("Fe",true) d.label4 = dialog.AddLabel("") d.bandatoms = dialog.AddCheckbox("Band Atoms",true) -- d.bandspace = dialog.AddCheckbox("Band To Space",false) d.str = dialog.AddSlider("Band Str",1.0,0.1,10,1) d.okay = dialog.AddButton("Band",1) d.cancel = dialog.AddButton("Cancel",0) local rc repeat rc = dialog.Show(d) until rc == 1 or rc == 0 if rc == 0 then print("User cancel") return end if d.Fe.value then dxFe = 1.38 dxS = 2.7488 dyS = 1.8488 end local nbands,iBand nbands = band.GetCount() if d.bandatoms.value and dxFe then local bandfunc = function(aa1,aa2,d) iBand = band.AddBetweenSegments(aa1,aa2,GetSulfurAtom(aa1),GetSulfurAtom(aa2)) band.SetGoalLength(iBand, d) end print("Banding cysteine sulfur distances") bandfunc(cluster[1][1],cluster[1][2],2 * dyS) bandfunc(cluster[2][1],cluster[2][2],2 * dyS) bandfunc(cluster[1][1],cluster[2][1],2 * dxS) bandfunc(cluster[1][2],cluster[2][2],2 * dxS) bandfunc(cluster[1][1],cluster[2][2],2 * math.sqrt(dyS*dyS+dxS*dxS)) bandfunc(cluster[2][1],cluster[1][2],2 * math.sqrt(dyS*dyS+dxS*dxS)) for iBand = band.GetCount()-5,band.GetCount() do band.SetStrength(iBand,d.str.value) end else print ("No action selected") end end function SideLength(r1,r2,a) -- law of cosines return math.sqrt( r1*r1 + r2*r2 - 2 *r1 *r2 * math.cos(a) ) end function Tetrahedral() print "M2cys4 Tetrahedral" cluster = BuildCluster({'c','c','c','c'}) if #cluster == 0 then return end -- Find best pair closest d = dialog.CreateDialog('MCys4 Positioning') d.label = dialog.AddLabel("If these are the wrong AAs, re-arrange the AAs") d.pair1 = dialog.AddLabel(string.format('Smallest Cluster: %d %d %d %d',unpack(cluster))) d.Label3 = dialog.AddLabel("Pick Metal Atom (the distances)") d.Fe = dialog.AddCheckbox("Fe",false) d.Zn = dialog.AddCheckbox("Zn",false) d.label2 = dialog.AddLabel("") d.bandatoms = dialog.AddCheckbox("Band Atoms",true) -- d.bandspace = dialog.AddCheckbox("Band To Space",false) d.str = dialog.AddSlider("Band Str",1.0,0.1,10,1) d.okay = dialog.AddButton("Band",1) d.cancel = dialog.AddButton("Cancel",0) local rc, MS, n repeat rc = dialog.Show(d) if rc == 0 then print("User cancel") return end n = 0 if d.Fe.value then MS = 2.35 n = n + 1 end if d.Zn.value then MS = 2.25 n = n + 1 end local nbands,iBand nbands = band.GetCount() if n > 1 then dialog.ShowMessageBox("Only pick one metal type","Oops") rc = -1 end until rc == 1 if d.bandatoms.value and n == 1 then local SS = math.sqrt(8./3.) * MS print("Banding cysteine sulfur distances") for i = 1,3 do for j = i+1,4 do iBand = band.AddBetweenSegments(cluster[i],cluster[j],GetSulfurAtom(cluster[i]),GetSulfurAtom(cluster[j])) if iBand == 0 then print("Could not band",cluster[i],cluster[j]," index",i,j) end band.SetGoalLength(iBand, SS) end end for iBand = band.GetCount()-5,band.GetCount() do band.SetStrength(iBand,d.str.value) end else print ("No action selected") end end function ZincFinger() print "Zn Cys2 Hist2 Zinc FInger" clustertype = { {'c','c','c','c'}, {'c','c','c','h'}, {'c','c','h','h'} } clusters = { BuildCluster(clustertype[1]), BuildCluster(clustertype[2]), BuildCluster(clustertype[3]) } if #clusters[1] == 0 and #clusters[2] == 0 and #clusters[3] == 0 then dialog.ShowMessageBox('No cluster found.','Awww.','Quit') print('No cluster found.') return end -- Find best pair closest d = dialog.CreateDialog('Zn Cys/His Positioning') d.label = dialog.AddLabel("If these are the wrong AAs, re-arrange the AAs") for i = 1,3 do if #clusters[i] > 0 then d['cluster'..tostring(i)] = dialog.AddLabel(string.format("Type %s: %d %d %d %d",table.concat(clustertype[i],''),unpack(clusters[i]))) end end d.Label3 = dialog.AddLabel("Pick cluster") for i = 1,3 do if #clusters[i] > 0 then d['type'..tostring(i)] = dialog.AddCheckbox(string.format("Type %s",table.concat(clustertype[i],'')),false) end end d.label2 = dialog.AddLabel("") d.bandatoms = dialog.AddCheckbox("Band Atoms",true) -- d.bandspace = dialog.AddCheckbox("Band To Space",false) d.str = dialog.AddSlider("Band Str",1.0,0.1,10,1) d.okay = dialog.AddButton("Band",1) d.cancel = dialog.AddButton("Cancel",0) local rc, MS, n, MH, cluster,angle repeat rc = dialog.Show(d) if rc == 0 then print("User cancel") return end n = 0 for i = 1, 3 do if d['type'..tostring(i)].value then cluster = clusters[i] n = n + 1 end end local nbands,iBand nbands = band.GetCount() if n > 1 then dialog.ShowMessageBox("Only pick one metal type","Oops") rc = -1 end until rc == 1 MS = 2.25 MH = 1.95 angle = math.acos(-1./3.) distances = {c={},h={}} distances['c']['c'] = SideLength(MS,MS,angle) distances['h']['h'] = SideLength(MH,MH,angle) distances['c']['h'] = SideLength(MS,MH,angle) distances['h']['c'] = distances['c']['h'] if d.bandatoms.value and n == 1 then local iBand print("Banding cysteine sulfur /histidine nitrogen distances") local atom = function(iseg) if structure.GetAminoAcid(iseg) == 'c' then return GetSulfurAtom(iseg) end return GetNitrogenAtom(iseg) end for i = 1,3 do for j = i+1,4 do iBand = band.AddBetweenSegments(cluster[i],cluster[j],atom(cluster[i]),atom(cluster[j])) if iBand == 0 then print("Could not band",cluster[i],cluster[j]," index",i,j) end band.SetGoalLength(iBand, distances[structure.GetAminoAcid(cluster[i])][structure.GetAminoAcid(cluster[j])]) end end for iBand = band.GetCount()-5,band.GetCount() do band.SetStrength(iBand,d.str.value) end else print ("No action selected") end end function Help() text = { "Sometimes the atoms of a protein bond to a metal", "ligand. Unlike the puzzles with large organic", "ligands, these small metal clusters are usually", "left out of the puzzle.", "", "These metal atoms usually bond to the sulfer in", "cysteines in simple geometric patterns. This", "recipe lets you pick a shape, and lets you", "band the sulfurs into the shape they would have", "if they were bonded to the metal.", "", "The script picks the tightest group. If you want", "a different group, you'll have to rearrange", "your protein." } return dialog.ShowMessageBox(text,title) end function PickLigand() d = dialog.CreateDialog(title) d.title = dialog.AddLabel('Pick Ligand Type (the shape):') d.label1 = dialog.AddLabel('M = metal ion') d.M2S2Cys4 = dialog.AddCheckbox('M2S2Cys4 ("twisted bowtie")',false) d.tetrahedral = dialog.AddCheckbox('MCys4 ("4-sided die")',false) d.zincfinger = dialog.AddCheckbox('ZnCys_4-n,Hist_n n=0,1,2 ("Zinc Finger")',false) d.Select = dialog.AddButton('Select',1) d.Cancel = dialog.AddButton('Cancel',0) d.Help = dialog.AddButton('Help',2) local rc repeat rc = dialog.Show(d) if rc == 0 then return end if rc == 1 and dialog.CountCheckedBoxes(d) ~= 1 then dialog.ShowMessageBox('Check exactly 1 ligand type','Oops','Okay') print(dialog.CountCheckedBoxes(d),"checked boxes") rc = -1 end if rc == 2 then if Help() == 0 then return end end until rc == 1 return dialog.FirstCheckedBox(d) end -- ================================== End Ligand Functions ligand = PickLigand() if ligand == nil then print ("No ligand selected or user cancelled") return end ligandfuncs = {} ligandfuncs.M2S2Cys4 = M2S2Cys4 ligandfuncs.tetrahedral = Tetrahedral ligandfuncs.zincfinger = ZincFinger ligandfuncs[ligand]()

Comments


brow42 Lv 1

Sometimes the atoms of a protein bond to a metal ligand. Unlike the puzzles with large organic ligands, these small metal clusters are usually left out of the puzzle.

These metal atoms usually bond to the sulfur in cysteines in simple geometric patterns. This recipe lets you pick a shape, and lets you band the sulfurs into the shape they would have if they were bonded to the metal.

Common in puzzles with names like porphyrin, ferredoxin, ruberedoxin.

  • Missing Ligand
    *
  • Residues should be bonding to a ligand, which is missing
  • The ligand could be replaced by constraints
  • If constraints are missing, then make bands
    *
  • Version 0.1 June 28 2013 brow42

  • Version 1.0 Nov 28 2013 brow42
  • Added tetrahedral symmetry
  • Will now work witih O-terminal cysteine
  • Added Help

Bruno Kestemont Lv 1

It identified ferredoxin on puzzel 814, but I don't see it on screen and I don't know what to do next. Which kind of view should we use?

brow42 Lv 1

The script cannot really guess what to do, so it relies on the user a lot.

Suppose the puzzle description says ferridoxin, and you have 5 cysteines. One type of ferridoxin bonds two iron and 4 cysteines together. You would check the 'M2S2Cys4' box.

The script knows what the positions of the atoms should be for this bonding (a flat rectangle). So it will bond the sulfurs of 4 cysteines with strong bands, with the lengths of the sides and diagonals of the rectangle.

Which 4 cysteines? The 4 that are most "grouped up". So, pick the 4 cysteines you think should be bonded to the iron and pull them together. Make two groups of two; the close pairs will be the short side of the rectangle. Make the fifth cysteine be far away. Now you can run the script and it will band the sulfurs.

If you don't know what kind of bond the protein has, you can experiment based on shape, like 4 cysteines bonded to one atom in a tetrahedron.

The script isn't smart enough to handle more than one ligand site. It has enough trouble with just the one!

There's no way to make the metal ion show up. It really is missing.