1# 2# * This library is free software; you can redistribute it and/or 3# * modify it under the terms of the GNU Lesser General Public 4# * License as published by the Free Software Foundation; either 5# * version 2.1 of the License, or (at your option) any later version. 6# * 7# * This library is distributed in the hope that it will be useful, 8# * but WITHOUT ANY WARRANTY; without even the implied warranty of 9# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 10# * Lesser General Public License for more details. 11# 12 13#propka3.0, revision 182 2011-08-09 14#------------------------------------------------------------------------------------------------------- 15#-- -- 16#-- PROPKA: A PROTEIN PKA PREDICTOR -- 17#-- -- 18#-- VERSION 3.0, 01/01/2011, COPENHAGEN -- 19#-- BY MATS H.M. OLSSON AND CHRESTEN R. SONDERGARD -- 20#-- -- 21#------------------------------------------------------------------------------------------------------- 22# 23# 24#------------------------------------------------------------------------------------------------------- 25# References: 26# 27# Very Fast Empirical Prediction and Rationalization of Protein pKa Values 28# Hui Li, Andrew D. Robertson and Jan H. Jensen 29# PROTEINS: Structure, Function, and Bioinformatics 61:704-721 (2005) 30# 31# Very Fast Prediction and Rationalization of pKa Values for Protein-Ligand Complexes 32# Delphine C. Bas, David M. Rogers and Jan H. Jensen 33# PROTEINS: Structure, Function, and Bioinformatics 73:765-783 (2008) 34# 35# PROPKA3: Consistent Treatment of Internal and Surface Residues in Empirical pKa predictions 36# Mats H.M. Olsson, Chresten R. Sondergard, Michal Rostkowski, and Jan H. Jensen 37# Journal of Chemical Theory and Computation, 7, 525-537 (2011) 38#------------------------------------------------------------------------------------------------------- 39import sys 40import string 41import math 42import copy 43import lib 44from pdb import Atom 45pka_print = lib.pka_print 46 47class Residue: 48 """ 49 Residue class - contains atoms and properties of the residue 50 """ 51 52 def __init__(self, atoms, resName=None, resNumb=None, chainID=None, resInfo=None, options=None): 53 """ 54 Constructer of the residue object. 55 """ 56 self.label = None 57 self.atoms = [] 58 if chainID == None: 59 self.chainID = atoms[0].chainID 60 else: 61 self.chainID = chainID 62 if resNumb == None: 63 self.resNumb = atoms[0].resNumb 64 else: 65 self.resNumb = resNumb 66 if resName == None: 67 self.resName = atoms[0].resName 68 else: 69 self.resName = resName 70 self.resType = None # determins the interaction parameters, e.g. 'COO' 71 self.Q = None # residue charge 72 self.type = None # 'amino-acid' / 'ion' / 'ligand' / 'N-terminus' / 'C-terminus' 73 self.buried = None 74 self.location= None 75 self.determinants = [[], [], []] 76 self.Nmass = 0 77 self.Nlocl = 0 78 self.Emass = 0.0 79 self.Vmass = 0.0 80 self.Elocl = 0.0 81 self.Vlocl = 0.0 82 self.x = 0.00 83 self.y = 0.00 84 self.z = 0.00 85 self.pKa_mod = None 86 self.pKa_pro = None 87 self.pKas = [] # list with several pKa-objects: not used yet 88 self.center_atoms = [] # list of atoms constituting the 'residue center', needed! 89 self.default_key = None # the 'default' configuration if not all are there 90 self.configurations = [] # keys to the atom configurations belonging to this residue 91 self.coupled_residues = [] 92 93 # setting residue information i.e. resType, type, Q, pKa_mod & pKa_pro from resInfo dictionary 94 self.setResidueInformation(resInfo=resInfo) 95 96 # setting residue label of the residue to create 97 self.setResidueLabel() 98 99 # setting up the atom labels used to calculate the 'residue center' 100 residue_center_atom_labels = lib.residueCenterAtomList(self.resName) 101 102 # adding/setting various atom-related properties such as atoms, configurations etc. 103 for atom in atoms: 104 105 # checking if this residue has 'HETATM' atoms; good chance it's a ligand then 106 if atom.type == "hetatm" and self.type == None: 107 self.type = "ligand" 108 109 # setting the number of configurations for this residue 110 for key in atom.configurations.keys(): 111 if key not in self.configurations: 112 self.configurations.append(key) 113 114 #if atom.name[0] != 'H': 115 if True: 116 atom.set_residue(self) 117 self.atoms.append(atom) 118 119 # setting 'center atoms'; needs to be on self since it is reset when you switch configurations 120 if len(residue_center_atom_labels) == 0: 121 self.center_atoms.append(atom) 122 elif atom.name in residue_center_atom_labels: 123 self.center_atoms.append(atom) 124 125 # set residue center, i.e. give 'x, y, z' values 126 if len(self.center_atoms) > 0: 127 self.setResidueCenter() 128 129 130 def setResidueInformation(self, resInfo=None): 131 """ 132 set residue information based on resName - it is set here since it is a convenience thing 133 """ 134 # resType - determines interaction parameters 135 if self.resName in resInfo['resType']: 136 self.resType = resInfo['resType'][self.resName] 137 138 # Q - group charge 139 if self.resName in resInfo['Q']: 140 self.Q = resInfo['Q'][self.resName] 141 else: 142 self.Q = 0.00 143 144 # type - 'amino-acid' / 'ion' / 'ligand' / 'N-terminus' / 'C-terminus' 145 if self.resName in lib.residueList("standard"): 146 self.type = "amino-acid" 147 elif self.resName in resInfo['type']: 148 self.type = resInfo['type'][self.resName] 149 150 # pKa_mod - model or water pKa value 151 if self.resName in resInfo['pKa']: 152 self.pKa_mod = resInfo['pKa'][self.resName] 153 self.pKa_pro = resInfo['pKa'][self.resName] 154 else: 155 self.pKa_mod = resInfo['pKa']['default'] 156 self.pKa_pro = resInfo['pKa']['default'] 157 158 159 def setConfiguration(self, key=None): 160 """ 161 set the 'current possition' to a specific 'configuration', or default if it doesn't exist 162 """ 163 self.cleanupPKA() 164 if key in self.configurations: 165 configuration = key 166 else: 167 configuration = self.default_key 168 169 for atom in self.atoms: 170 atom.setConfiguration(key=configuration) 171 self.setResidueCenter() 172 173 174 def cleanupPKA(self): 175 """ 176 Initializing/cleaning up residue! 177 """ 178 self.Nmass = 0 179 self.Nlocl = 0 180 self.Emass = 0.00 181 self.Elocl = 0.00 182 self.buried = 0.00 183 self.pKa_pro = self.pKa_mod 184 self.determinants = [[], [], []] 185 186 187 def setResidueCenter(self): 188 """ 189 sets the center of the residue based on center_atoms 190 """ 191 number_of_atoms = len(self.center_atoms) 192 self.x = 0.00 193 self.y = 0.00 194 self.z = 0.00 195 for atom in self.center_atoms: 196 self.x += atom.x 197 self.y += atom.y 198 self.z += atom.z 199 if number_of_atoms > 0: 200 self.x = self.x/number_of_atoms 201 self.y = self.y/number_of_atoms 202 self.z = self.z/number_of_atoms 203 204 205 def getThirdAtomInAngle(self, atom=None): 206 """ 207 finds and returns the third atom in angular dependent interactions 208 expecting one of ["HIS", "ARG", "AMD", "TRP"] 209 """ 210 if self.resName == "HIS": 211 if atom.name == "HD1": 212 return self.getAtom(name="ND1") 213 elif atom.name == "HE2": 214 return self.getAtom(name="NE2") 215 elif self.resName == "ARG": 216 if atom.name in ["HE"]: 217 return self.getAtom(name="NE") 218 elif atom.name in ["1HH1", "2HH1"]: 219 return self.getAtom(name="NH1") 220 elif atom.name in ["1HH2", "2HH2"]: 221 return self.getAtom(name="NH2") 222 elif self.resName == "ASN": 223 return self.getAtom(name="ND2") 224 elif self.resName == "GLN": 225 return self.getAtom(name="NE2") 226 elif self.resName == "TRP": 227 return self.getAtom(name="NE1") 228 229 230 def getAtom(self, name=None): 231 """ 232 finds and returns the specified atom in this residue. 233 """ 234 for atom in self.atoms: 235 if atom.name == name: 236 return atom 237 break 238 239 return None 240 241 242 def checkOXT(self): 243 """ 244 Checks that OXT is present or creates it. 245 """ 246 O = self.getAtom(name="O") 247 OXT = self.getAtom(name="OXT") 248 249 # NMR Xplor over-write 250 if O == None: 251 O = self.getAtom(name="OT1") 252 if O != None: 253 O.setProperty(name="O") 254 if OXT == None: 255 OXT = self.getAtom(name="OT2") 256 if OXT != None: 257 OXT.setProperty(name="OXT") 258 259 # continuing after 'NMR exception'; creating OXT if not found 260 if OXT == None: 261 # did not find OXT, creating it 'on the fly' 262 CA = self.getAtom(name="CA") 263 C = self.getAtom(name="C") 264 if O == None or CA == None or C == None: 265 pka_print("ERROR: cannot create OXT atom - missing CA, C, or O atoms; please correct pdbfile"); sys.exit(8) 266 dX = -((CA.x-C.x) + (O.x-C.x)) 267 dY = -((CA.y-C.y) + (O.y-C.y)) 268 dZ = -((CA.z-C.z) + (O.z-C.z)) 269 distance = math.sqrt( dX*dX + dY*dY + dZ*dZ ) 270 x = C.x + 1.23*dX/distance 271 y = C.y + 1.23*dY/distance 272 z = C.z + 1.23*dZ/distance 273 OXT = C.makeCopy(name="OXT", x=x, y=y, z=z) 274 self.atoms.append(OXT) 275 pka_print("creating %s atom" % (OXT.name)) 276 277 return [O, OXT] 278 279 280 281 282 def fillUnknownConfigurations(self, keys=None, options=None): 283 """ 284 Fills in the configurations that have not been read 285 """ 286 # getting default key as the first OK element in the sorted protein keys 287 for key in keys: 288 if key in self.configurations: 289 self.default_key = key 290 break 291 292 # enforcing all residue keys on each atom 293 for atom in self.atoms: 294 for configuration in self.configurations: 295 if key not in atom.configurations: 296 atom.configurations[configuration] = atom.configurations[self.default_key] 297 298 299 def checked(self, options=None): 300 """ 301 Checks that I understand all residues. 302 """ 303 excluded_resNames = lib.residueList("excluded") 304 if self.resName in excluded_resNames: 305 return False 306 else: 307 return True 308 309 310 def checkResidue(self, options=None): 311 """ 312 Checks that I understand all residues. 313 """ 314 315 residue_list = lib.residueList("all") 316 rename = {"LYP": "LYS", 317 "CYX": "CYS", 318 "HSD": "HIS", 319 } 320 321 # renaming residue if in rename dictionary 322 if self.resName in rename: 323 newName = rename[self.resName] 324 if options.verbose == True: 325 pka_print("Warning: renaming %s to %s" % (self.resName, newName)) 326 self.resName = newName 327 for atom in self.atoms: 328 atom.resName = newName 329 else: 330 """OK - lets rock""" 331 332 # after rename residue cases, check heavy atoms 333 if self.resName in residue_list: 334 # OK - lets rock 335 self.checkAtoms(options=options) 336 # Chresten's stuff 337 elif self.type == "ion": 338 outstr = "%s%4d - OK %s" % (self.resName, self.resNumb, self.type) 339 pka_print(outstr) 340 #elif self.resName in version.ions.keys(): 341 # str = "%-3s%4d - %s with charge %+d" % (self.resName, 342 # self.resNumb, 343 # version.ions_long_names[self.resName], 344 # version.ions[self.resName]) 345 # pka_print(str) 346 else: 347 outstr = "%s%4d - unidentified residue" % (self.resName, self.resNumb) 348 pka_print(outstr) 349 350 351 def checkAtoms(self, options=None): 352 """ 353 Checks that all heavy atoms are there 354 """ 355 outstr = "%s%4d - " % (self.resName, self.resNumb) 356 atom_list = lib.atomList(self.resName) 357 OK = True 358 for name in atom_list: 359 FOUND = False 360 for atom in self.atoms: 361 if atom.name == name: 362 FOUND = True 363 if FOUND == False: 364 outstr += " %s" % (name) 365 OK = False 366 367 if OK == True: 368 self.checkConfigurations(verbose=False) 369 outstr += " OK (%2d: %2d)" % (len(self.atoms), len(self.configurations)) 370 if options.verbose == True: 371 pka_print(outstr) 372 else: 373 outstr += " missing" 374 pka_print(outstr) 375 376 377 def checkConfigurations(self, verbose=False): 378 """ 379 checks that all atoms in this residue has the same number of configurations 380 """ 381 for atom in self.atoms: 382 for key in self.configurations: 383 if key not in atom.configurations: 384 atom.configurations[key] = atom.configurations[self.default_key] 385 386 387 def printLabel(self): 388 """ 389 prints the residue ID 390 """ 391 outstr = "%s%4d %s" % (self.resName, self.resNumb, self.chainID) 392 pka_print(outstr) 393 394 395 def __str__(self): 396 return self.label 397 398 def extractBackBoneAtoms(self): 399 """ 400 Returns the back-bone atoms 401 """ 402 N = None 403 H = None 404 C = None 405 O = None 406 for atom in self.atoms: 407 if atom.name == "N": 408 N = atom 409 elif atom.name == "H": 410 H = atom 411 elif atom.name == "C": 412 C = atom 413 elif atom.name == "O": 414 O = atom 415 416 return N, H, C, O 417 418 419 def makeDeterminantAtomList(self, resType=None, type=None, version=None): 420 """ 421 Extracting reference to determinant atom - test stage still 422 """ 423 if type == 'base' or type == 'acid': 424 pair_type = type 425 else: 426 if resType in ["HIS", "LYS", "ARG", "N+ "]: 427 pair_type = 'base' 428 else: 429 pair_type = 'acid' 430 431 if self.resName not in version.atomInteractionList[pair_type]: 432 pka_print("cannot find atomInteractionList for residue %s in residue.makeDeterminantAtomList()" % (self.resName)) 433 sys.exit(9) 434 435 # Searching for determinant atom 436 atoms = [] 437 for atom in self.atoms: 438 if atom.name in version.atomInteractionList[pair_type][self.resName]: 439 atoms.append(atom) 440 441 return atoms 442 443 444 def calculateTotalPKA(self): 445 """ 446 Calculates the total pKa values from the desolvation and determinants 447 """ 448 back_bone = 0.00 449 for determinant in self.determinants[0]: 450 value = determinant.value 451 back_bone += value 452 453 side_chain = 0.00 454 for determinant in self.determinants[1]: 455 value = determinant.value 456 side_chain += value 457 458 coulomb = 0.00 459 for determinant in self.determinants[2]: 460 value = determinant.value 461 coulomb += value 462 463 self.pKa_pro = self.pKa_mod + self.Emass + self.Elocl + back_bone + side_chain + coulomb 464 465 466 def calculateIntrinsicPKA(self): 467 """ 468 Calculates the intrinsic pKa values from the desolvation determinants, back-bone hydrogen bonds, 469 and side-chain hydrogen bond to non-titratable residues 470 """ 471 back_bone = 0.00 472 for determinant in self.determinants[1]: 473 value = determinant.value 474 back_bone += value 475 476 side_chain = 0.00 477 for determinant in self.determinants[0]: 478 if determinant.label[0:3] not in ['ASP','GLU','LYS','ARG','HIS','CYS','TYR','C- ','N+ ']: 479 value = determinant.value 480 side_chain += value 481 482 self.intrinsic_pKa = self.pKa_mod + self.Emass + self.Elocl + back_bone + side_chain 483 484 return 485 486 487 488 def calculateDesolvation(self, atoms, version=None, options=None): 489 """ 490 Calculates the desolvation contribution 491 """ 492 version.calculateDesolvation(self, atoms, options=options) 493 494 495 def setChain(self, chainID): 496 """ 497 Set a chainID 498 """ 499 self.chainID = chainID 500 for atom in self.atoms: 501 atom.chainID = chainID 502 503 504 def setResidueNumber(self, resNumb): 505 """ 506 Set the residue numbers to 'resNumb' 507 """ 508 self.resNumb = resNumb 509 510 511 def setResidueLabel(self, label=None): 512 """ 513 Set the residue label to e.g. 'GLU 145 A' 514 """ 515 if label == None: 516 self.label = lib.makeResidueLabel(self.resName, self.resNumb, self.chainID) 517 else: 518 self.label = label 519 520 521 def shiftResidueNumber(self, shift): 522 """ 523 Shift the residue numbers with 'shift' 524 """ 525 self.resNumb = self.resNumb + shift 526 527 528 def calculateFoldingEnergy(self, pH=None, reference=None, options=None): 529 """ 530 returning the electrostatic energy of this residue at pH 'pH' 531 """ 532 if pH == None: 533 pH = options.pH 534 if reference == None: 535 reference = options.reference 536 537 # calculating the ddG(neutral --> low-pH) contribution 538 if self.resType not in ["COO", "HIS", "N+ ", "CYS", "TYR", "LYS", "ARG"]: 539 ddG = 0.00 540 else: 541 if reference == "low-pH": 542 ddG_neutral = 0.00 543 else: 544 if self.Q > 0.00: 545 pKa_prime = self.pKa_pro 546 coulomb_determinants = self.determinants[2] 547 for determinant in coulomb_determinants: 548 if determinant.value > 0.00: 549 pKa_prime -= determinant.value 550 ddG_neutral = -1.36*(pKa_prime - self.pKa_mod) 551 else: 552 ddG_neutral = 0.00 553 554 # calculating the ddG(low-pH --> pH) contribution 555 # folded 556 x = pH - self.pKa_pro 557 y = 10**x 558 Q_pro = math.log10(1+y) 559 560 # unfolded 561 x = pH - self.pKa_mod 562 y = 10**x 563 Q_mod = math.log10(1+y) 564 565 ddG_low = -1.36*(Q_pro - Q_mod) 566 ddG = ddG_neutral + ddG_low 567 568 return ddG 569 570 571 def getCharge(self, pH, state): 572 """ 573 returning the charge of this residue at pH 'pH' 574 """ 575 if state == "mod" or state == "unfolded": 576 x = self.Q*(self.pKa_mod - pH) 577 else: 578 x = self.Q*(self.pKa_pro - pH) 579 #x = pH - self.pKa_pro 580 y = 10**x 581 charge = self.Q*(y/(1.0+y)) 582 #charge = math.log10(1+y) 583 584 return charge 585 586 587 def calculateTitrationCurve(self, grid=[0., 14., 0.10]): 588 """ 589 calculates the titration curve of this residue 590 """ 591 if grid == None: 592 grid = [self.pKa_pro-2.5, self.pKa_pro+2.5, 0.10] 593 state = "folded" 594 titration_curve = [] 595 pH = grid[0] 596 stop = grid[1] + grid[2]/2.0 597 while pH < stop: 598 Q = self.getCharge(pH, state) 599 titration_curve.append([pH, Q]) 600 pH += grid[2] 601 602 return titration_curve 603 604 605 def getSummaryString(self): 606 """ 607 Writing the summary string 608 """ 609 outstr = " %s%8.2lf%10.2lf" % (self.label, self.pKa_pro, self.pKa_mod) 610 return outstr 611 612 613 def mutateToAla(self): 614 """ 615 mutating residue to 'ALA' 616 Note, if you mutate a ionizable residue to Ala it will remain in 'ionizable_residues list' 617 and propka will try to calcualte the pKa of it !!! 618 """ 619 keep_atoms = lib.atomList("ALA") 620 self.printLabel() 621 self.resName = "ALA" 622 self.setResidueLabel() 623 self.printLabel() 624 new_atoms = [] 625 for atom in self.atoms: 626 if atom.name in keep_atoms: 627 new_atoms.append(atom) 628 pka_print(self.atoms) 629 pka_print(new_atoms) 630 self.cleanupResidue 631 self.pKa_mod = pKa_mod(self.resName) 632 self.pKa_pro = self.pKa_mod 633 self.atoms = new_atoms 634 635 636 def replaceWithResidue(self, new_residue): 637 """ 638 replacing current residue with incoming residue 639 """ 640 self.cleanupResidue() 641 self.resName = new_residue.resName 642 self.pKa_mod = new_residue.pKa_mod 643 self.setResidueLabel() 644 exclude_atoms = ["N", "CA", "C", "O"] 645 tmp_atoms = [] 646 for atom in self.atoms: 647 if atom.name in exclude_atoms: 648 tmp_atoms.append(atom) 649 self.atoms = tmp_atoms 650 for new_atom in new_residue.atoms: 651 if new_atom.name in exclude_atoms: 652 """ do nothing """ 653 else: 654 self.atoms.append(new_atom) 655 656 657 def getDeterminantString(self): 658 """ 659 Everything should be calculated, now, let's print the darn thing and be done! 660 """ 661 #if self.location == "SURFACE" or self.location == "BURIED ": 662 BURIED_RATIO = True 663 664 empty_determinant = "%s%4d%2s" % ("XXX", 0, "X") 665 number_of_sidechain = len(self.determinants[0]) 666 number_of_backbone = len(self.determinants[1]) 667 number_of_coulomb = len(self.determinants[2]) 668 number_of_determinants = number_of_sidechain + number_of_backbone + number_of_coulomb 669 number_of_lines = max(1, number_of_sidechain, number_of_backbone, number_of_coulomb) 670 outsting = "" 671 #outsting += " number_of_sidechain = %d" % (number_of_sidechain) 672 #outsting += " number_of_backbone = %d" % (number_of_backbone ) 673 #outsting += " number_of_coulomb = %d" % (number_of_coulomb ) 674 #outsting += " number_of_lines = %d" % (number_of_lines ) 675 #print outsting 676 677 for line_number in range(1, number_of_lines+1): 678 outsting += "%s" % (self.label) 679 if line_number == 1: 680 outsting += " %6.2lf" % (self.pKa_pro) 681 if len(self.coupled_residues)>0: 682 outsting+='*' 683 else: 684 outsting+=' ' 685 686 if BURIED_RATIO == True: 687 if self.type == "BONDED": 688 outsting += " BONDED " 689 else: 690 outsting += " %4d%2s " % (int(100.0*self.buried), "%") 691 else: 692 outsting += "%8s" % (self.type) 693 outsting += " %6.2lf %4d" % (self.Emass, self.Nmass) 694 outsting += " %6.2lf %4d" % (self.Elocl, self.Nlocl) 695 else: 696 outsting += "%40s" % (" ") 697 698 # Side-chain determinants 699 if line_number > number_of_sidechain : 700 outsting += "%8.2lf %s" % (0.0, empty_determinant) 701 else: 702 determinant = self.determinants[0][line_number-1] 703 outsting += "%8.2lf %s" % (determinant.value, determinant.label) 704 705 # Back-bone determinants 706 if line_number > number_of_backbone: 707 outsting += "%8.2lf %s" % (0.0, empty_determinant) 708 else: 709 determinant = self.determinants[1][line_number-1] 710 outsting += "%8.2lf %s" % (determinant.value, determinant.label) 711 712 # Coulomb determinants 713 if line_number > number_of_coulomb: 714 outsting += "%8.2lf %s" % (0.0, empty_determinant) 715 else: 716 determinant = self.determinants[2][line_number-1] 717 outsting += "%8.2lf %s" % (determinant.value, determinant.label) 718 719 # adding end-of-line 720 outsting += "\n" 721 722 return outsting 723 724 725 def printResult(self): 726 """ 727 Everything should be calculated, now, let's print the darn thing and be done! 728 """ 729 #if self.location == "SURFACE" or self.location == "BURIED ": 730 BURIED_RATIO = True 731 732 empty_determinant = "%s%4d%2s" % ("XXX", 0, "X") 733 number_of_sidechain = len(self.determinants[0]) 734 number_of_backbone = len(self.determinants[1]) 735 number_of_coulomb = len(self.determinants[2]) 736 number_of_determinants = number_of_sidechain + number_of_backbone + number_of_coulomb 737 number_of_lines = max(1, number_of_sidechain, number_of_backbone, number_of_coulomb) 738 outstr = "" 739 outstr += " number_of_sidechain = %d" % (number_of_sidechain) 740 outstr += " number_of_backbone = %d" % (number_of_backbone ) 741 outstr += " number_of_coulomb = %d" % (number_of_coulomb ) 742 outstr += " number_of_lines = %d" % (number_of_lines ) 743 #print outstr 744 745 if True: 746 for line_number in range(1, number_of_lines+1): 747 outstr = "%s%4d%2s" % (self.resName, self.resNumb, self.chainID) 748 if line_number == 1: 749 outstr += " %6.2lf" % (self.pKa_pro) 750 if BURIED_RATIO == True: 751 outstr += " %4d%2s " % (int(100.0*self.buried), "%") 752 else: 753 outstr += " %8s" % (self.type) 754 outstr += " %6.2lf %4d" % (self.Emass, self.Nmass) 755 outstr += " %6.2lf %4d" % (self.Elocl, self.Nlocl) 756 else: 757 outstr += "%40s" % (" ") 758 759 # Side-chain determinant 760 if line_number > number_of_sidechain : 761 outstr += "%8.2lf %s" % (0.0, empty_determinant) 762 else: 763 determinant = self.determinants[0][line_number-1] 764 outstr += "%8.2lf %s" % (determinant.value, determinant.label) 765 766 # Back-bone determinant 767 if line_number > number_of_backbone: 768 outstr += "%8.2lf %s" % (0.0, empty_determinant) 769 else: 770 determinant = self.determinants[1][line_number-1] 771 outstr += "%8.2lf %s" % (determinant.value, determinant.label) 772 773 # Coulomb determinant 774 if line_number > number_of_coulomb: 775 outstr += "%8.2lf %s" % (0.0, empty_determinant) 776 else: 777 determinant = self.determinants[2][line_number-1] 778 outstr += "%8.2lf %s" % (determinant.value, determinant.label) 779 pka_print('%s' % (outstr)) 780 else: 781 outstr = "%s%4d%2s%4d%2d" % (self.resName, self.resNumb, self.chainID, number_of_lines, number_of_determinants) 782 pka_print('%s' % (outstr)) 783 784 pka_print('') 785 786 787 def translate(self, translation): 788 """ 789 translate residue according to 'translation' 790 """ 791 for atom in self.atoms: 792 atom.x += translation[0] 793 atom.y += translation[1] 794 atom.z += translation[2] 795 for key in atom.configurations.keys(): 796 for i in range(3): 797 atom.configurations[key][i] += translation[i] 798 799 800 def rotate(self, axis, theta, center=None): 801 """ 802 rotate residue theta radians around axis with center=center 803 """ 804 from rotate import generalRotationMatrix 805 translate = [0.00, 0.00, 0.00] 806 number_of_atoms = 0 807 for atom in self.atoms: 808 if atom.name in center or center == None: 809 number_of_atoms += 1 810 translate[0] += atom.x/len(self.atoms) 811 translate[1] += atom.y/len(self.atoms) 812 translate[2] += atom.z/len(self.atoms) 813 for atom in self.atoms: 814 for i in range(3): 815 translate[i] = translate[i]/number_of_atoms 816 817 # translate to rotation center 818 for atom in self.atoms: 819 atom.x -= translate[0] 820 atom.y -= translate[1] 821 atom.z -= translate[2] 822 823 # get rotation matrix 824 rotation_matrix = generalRotationMatrix(axis, theta) 825 826 # rotating 827 new_position = [None, None, None] 828 for atom in self.atoms: 829 830 # rotate actual position 831 old_position = [atom.x, atom.y, atom.z] 832 for xyz in range(3): 833 new_position[xyz] = translate[xyz] 834 for i in range(3): 835 new_position[xyz] += rotation_matrix[xyz][i]*old_position[i] 836 # update position 837 atom.x = new_position[0] 838 atom.y = new_position[1] 839 atom.z = new_position[2] 840 841 # rotate configuration 842 for key in atom.configurations.keys(): 843 for xyz in range(3): 844 new_position[xyz] = translate[xyz] 845 for i in range(3): 846 new_position[xyz] += rotation_matrix[xyz][i]*atom.configurations[key][i] 847 for xyz in range(3): 848 atom.configurations[key][xyz] = new_position[xyz] 849 850 851 def makeCopy(self, 852 chainID = None, 853 resNumb = None, 854 ): 855 """ 856 making a copy of this residue 857 """ 858 from protein import getResidueParameters 859 if chainID == None: chainID = self.chainID 860 if resNumb == None: resNumb = self.resNumb 861 862 newAtoms = [] 863 for atom in self.atoms: 864 newAtoms.append( atom.makeCopy(chainID=chainID, resNumb=resNumb) ) 865 866 resInfo = getResidueParameters() 867 newResidue = Residue(newAtoms, resInfo=resInfo) 868 newResidue.resType = self.resType 869 newResidue.Q = self.Q 870 newResidue.type = self.type 871 872 return newResidue 873 874 875