# # Class for storing groups important for propka calculations # from __future__ import division from __future__ import print_function import Source.ligand, Source.determinant, Source.ligand_pka_values, math, Source.protonate my_protonator = Source.protonate.Protonate(verbose=False) expected_atoms_acid_interactions = { 'COO':{'O':2}, 'HIS':{'H':2, 'N':2}, 'CYS':{'S':1}, 'TYR':{'O':1}, 'LYS':{'N':1}, 'ARG':{'H':5, 'N':3}, 'ROH':{'O':1}, 'AMD':{'H':2, 'N':1}, 'TRP':{'H':1, 'N':1}, 'N+': {'N':1}, 'C-': {'O':2}, 'BBN':{'H':1, 'N':1,}, 'BBC':{'O':1}, 'NAR':{'H':1, 'N':1}, 'NAM':{'H':1, 'N':1}, 'F': {'F':1}, 'Cl': {'Cl':1}, 'OH': {'H':1, 'O':1}, 'OP': {'O':1}, 'O3': {'O':1}, 'O2': {'O':1}, 'SH': {'S':1}, 'CG': {'H':5, 'N':3}, 'C2N':{'H':4, 'N':2}, 'OCO':{'O':2}, 'N30':{'H':4, 'N':1}, 'N31':{'H':3, 'N':1}, 'N32':{'H':2, 'N':1}, 'N33':{'H':1, 'N':1}, 'NP1':{'H':2, 'N':1}, 'N1' :{'N':1} } expected_atoms_base_interactions = { 'COO':{'O':2}, 'HIS':{'N':2}, 'CYS':{'S':1}, 'TYR':{'O':1}, 'LYS':{'N':1}, 'ARG':{'N':3}, 'ROH':{'O':1}, 'AMD':{'O':1}, 'TRP':{'N':1}, 'N+': {'N':1}, 'C-': {'O':2}, 'BBN':{'H':1, 'N':1,}, 'BBC':{'O':1}, 'NAR':{'H':1, 'N':1}, 'NAM':{'H':1, 'N':1}, 'F': {'F':1}, 'Cl': {'Cl':1}, 'OH': {'H':1, 'O':1}, 'OP': {'O':1}, 'O3': {'O':1}, 'O2': {'O':1}, 'SH': {'S':1}, 'CG': {'N':3}, 'C2N':{'N':2}, 'OCO':{'O':2}, 'N30':{'N':1}, 'N31':{'N':1}, 'N32':{'N':1}, 'N33':{'N':1}, 'NP1':{'N':1}, 'N1' :{'N':1} } class Group: def __init__(self, atom): #print('Made new %s group from %s'%(type,atom)) self.atom = atom self.type = '' atom.group = self # set up data structures self.determinants = {'sidechain':[],'backbone':[],'coulomb':[]} self.pka_value = 0.0 self.model_pka = 0.0 self.Emass = 0.0 self.Nmass = 0.0 self.Elocl = 0.0 self.Nlocl = 0.0 self.buried = 0.0 self.x = 0.0 self.y = 0.0 self.z = 0.0 self.charge = 0 self.interaction_atoms_for_acids = [] self.interaction_atoms_for_bases = [] self.model_pka_set = False # information on covalent and non-covalent coupling self.non_covalently_coupled_groups = [] self.covalently_coupled_groups = [] self.coupled_titrating_group = None self.common_charge_centre = False self.residue_type = self.atom.resName if self.atom.terminal: self.residue_type = self.atom.terminal if self.atom.type=='atom': self.label = '%-3s%4d%2s'%(self.residue_type, atom.resNumb, atom.chainID) elif self.atom.resName in ['DA ','DC ','DG ','DT ']: self.label = '%1s%1s%1s%4d%2s'%(self.residue_type[1], atom.element, atom.name.replace('\'','')[-1], atom.resNumb, atom.chainID) # self.label = '%1s%1s%1s%4d%2s'%(self.residue_type[1], atom.element,atom.name[-1], atom.resNumb, atom.chainID) else: self.label = '%-3s%4s%2s'%(self.residue_type, atom.name, atom.chainID) # container for squared distances self.squared_distances = {} return # # Coupling-related methods # def couple_covalently(self, other): """ Couple this group with another group """ # do the coupling if not other in self.covalently_coupled_groups: self.covalently_coupled_groups.append(other) if not self in other.covalently_coupled_groups: other.covalently_coupled_groups.append(self) return def couple_non_covalently(self, other): """ Couple this group with another group """ # do the coupling if not other in self.non_covalently_coupled_groups: self.non_covalently_coupled_groups.append(other) if not self in other.non_covalently_coupled_groups: other.non_covalently_coupled_groups.append(self) return def get_covalently_coupled_groups(self): return self.covalently_coupled_groups def get_non_covalently_coupled_groups(self): return self.non_covalently_coupled_groups def share_determinants(self, others): # for each determinant type for other in others: if other == self: continue for type in ['sidechain','backbone','coulomb']: for g in other.determinants[type]: self.share_determinant(g,type) # recalculate pka values self.calculate_total_pka() other.calculate_total_pka() return def share_determinant(self, new_determinant, type): added = False # first check if we already have a determinant with this label for own_determinant in self.determinants[type]: if own_determinant.group == new_determinant.group: # if so, find the average value avr = (own_determinant.value + new_determinant.value)/2.0 own_determinant.value = avr new_determinant.value = avr added = True # otherwise we just add the determinant to our list if not added: self.determinants[type].append(Source.determinant.Determinant(new_determinant.group, new_determinant.value)) return def make_covalently_coupled_line(self): # first check if there are any coupled groups at all if len(self.covalently_coupled_groups) == 0: return '' line = 'CCOUPL%5d'%self.atom.numb # extract and sort numbers of coupled groups coupled = [] for g in self.covalently_coupled_groups: coupled.append(g.atom.numb) coupled.sort() # write 'em out for b in coupled: line += '%5d'%b line += '\n' return line def make_non_covalently_coupled_line(self): # first check if there are any coupled groups at all if len(self.non_covalently_coupled_groups) == 0: return '' line = 'NCOUPL%5d'%self.atom.numb # extract and sort numbers of coupled groups coupled = [] for g in self.non_covalently_coupled_groups: coupled.append(g.atom.numb) coupled.sort() # write 'em out for b in coupled: line += '%5d'%b line += '\n' return line # # Bookkeeping methods # def __eq__(self, other): """ Check if two groups should be considered identical """ if self.atom.type == 'atom': # In case of protein atoms we trust the labels return self.label==other.label else: # For heterogene atoms we also need to check the residue number return self.label==other.label and self.atom.resNumb == other.atom.resNumb def __hash__(self): """ Needed together with __eq__ - otherwise we can't make sets of groups """ return id(self) def __iadd__(self, other): if self.type != other.type: raise Exception('Cannot add groups of different types (%s and %s)'%(self.type,other.type)) # add all values self.pka_value += other.pka_value self.Nmass += other.Nmass self.Emass += other.Emass self.Nlocl += other.Nlocl self.Elocl += other.Elocl self.buried += other.buried # and add all determinants for type in ['sidechain','backbone','coulomb']: for determinant in other.determinants[type]: self.add_determinant(determinant, type) return self def add_determinant(self, new_determinant, type): """ Adds to current and creates non-present determinants""" # first check if we already have a determinant with this label for own_determinant in self.determinants[type]: if own_determinant.group == new_determinant.group: # if so, add the value own_determinant.value += new_determinant.value return # otherwise we just add the determinant to our list self.determinants[type].append(Source.determinant.Determinant(new_determinant.group, new_determinant.value)) return def set_determinant(self, new_determinant, type): """ Overwrites current and creates non-present determinants""" # first check if we already have a determinant with this label for own_determinant in self.determinants[type]: if own_determinant.group == new_determinant.group: # if so, overwrite the value own_determinant.value = new_determinant.value return # otherwise we just add the determinant to our list self.determinants[type].append(Source.determinant.Determinant(new_determinant.group, new_determinant.value)) return def remove_determinants(self, labels): """ removes all determinants with label in labels """ for type in ['sidechain','backbone','coulomb']: matches = list(filter(lambda d: d.label in labels, [d for d in self.determinants[type]])) for m in matches: self.determinants[type].remove(m) return def __truediv__(self, value): value = float(value) # divide all values self.pka_value /= value self.Nmass /= value self.Emass /= value self.Nlocl /= value self.Elocl /= value self.buried /= value # and all determinants for type in ['sidechain','backbone','coulomb']: for determinant in self.determinants[type]: determinant.value /= value return self def clone(self): res = Group(self.atom) res.type = self.type res.residue_type = self.residue_type res.model_pka = self.model_pka res.coupled_titrating_group = self.coupled_titrating_group res.covalently_coupled_groups = self.covalently_coupled_groups res.non_covalently_coupled_groups = self.non_covalently_coupled_groups res.titratable = self.titratable res.charge = self.charge return res def setup(self): # set the charges if self.type in self.parameters.charge.keys(): self.charge = self.parameters.charge[self.type] if self.residue_type in self.parameters.ions.keys(): self.charge = self.parameters.ions[self.residue_type] #print('ION setup',self,self.residue_type, self.charge) # find the center and the interaction atoms self.setup_atoms() # set the model pka value self.titratable = False if self.residue_type in self.parameters.model_pkas.keys(): if not self.model_pka_set: self.model_pka = self.parameters.model_pkas[self.residue_type] # check if we should apply a custom model pka key = '%s-%s'%(self.atom.resName.strip(), self.atom.name.strip()) if key in self.parameters.custom_model_pkas.keys(): self.model_pka = self.parameters.custom_model_pkas[key] self.model_pka_set = True if self.model_pka_set and not self.atom.cysteine_bridge: self.titratable = True return def setup_atoms(self): # This method is overwritten for some types of groups # set the center at the position of the main atom self.set_center([self.atom]) # set the main atom as interaction atom self.set_interaction_atoms([self.atom], [self.atom]) return def set_interaction_atoms(self, interaction_atoms_for_acids, interaction_atoms_for_bases): [a.set_group_type(self.type) for a in interaction_atoms_for_acids+interaction_atoms_for_bases] self.interaction_atoms_for_acids = interaction_atoms_for_acids self.interaction_atoms_for_bases = interaction_atoms_for_bases # check if all atoms have been identified ok = True for [expect, found, t] in [[expected_atoms_acid_interactions, self.interaction_atoms_for_acids, 'acid'], [expected_atoms_base_interactions, self.interaction_atoms_for_bases, 'base']]: if self.type in expect.keys(): for e in expect[self.type].keys(): if len([a for a in found if a.element==e]) != expect[self.type][e]: ok = False if not ok: print('Warning: Missing atoms or failed protonation for %s (%s) -- please check the structure'%(self.label, self.type)) print(' %s'%self) Na = sum([expected_atoms_acid_interactions[self.type][e] for e in expected_atoms_acid_interactions[self.type].keys()]) Nb = sum([expected_atoms_base_interactions[self.type][e] for e in expected_atoms_base_interactions[self.type].keys()]) print(' Expected %d interaction atoms for acids, found:'%Na) for i in range(len(self.interaction_atoms_for_acids)): print(' %s'%self.interaction_atoms_for_acids[i]) print(' Expected %d interaction atoms for bases, found:'%Nb) for i in range(len(self.interaction_atoms_for_bases)): print(' %s'%self.interaction_atoms_for_bases[i]) #return return def get_interaction_atoms(self, interacting_group): if interacting_group.residue_type in self.parameters.base_list: return self.interaction_atoms_for_bases else: return self.interaction_atoms_for_acids #default is acid interaction atoms - cf. 3.0 def set_center(self, atoms): # reset center self.x = 0.0; self.y = 0.0; self.z = 0.0 # find the average positon of atoms for atom in atoms: self.x += atom.x; self.y += atom.y; self.z += atom.z self.x /= float(len(atoms)) self.y /= float(len(atoms)) self.z /= float(len(atoms)) return def getDeterminantString(self, remove_penalised_group=False): if self.coupled_titrating_group and remove_penalised_group: return '' number_of_sidechain = len(self.determinants['sidechain']) number_of_backbone = len(self.determinants['backbone']) number_of_coulomb = len(self.determinants['coulomb']) number_of_lines = max(1, number_of_sidechain, number_of_backbone, number_of_coulomb) str = "" for line_number in range(number_of_lines): str += "%s" % (self.label) if line_number == 0: str += " %6.2lf" %(self.pka_value) if len(self.non_covalently_coupled_groups)>0: str+='*' else: str+=' ' # if self.atom.cysteine_bridge: # str += " BONDED " # else: str += " %4d%2s " % (int(100.0*self.buried), "%") str += " %6.2lf %4d" % (self.Emass, self.Nmass) str += " %6.2lf %4d" % (self.Elocl, self.Nlocl) else: str += "%40s" % (" ") # add the determinants for type in ['sidechain','backbone','coulomb']: str += self.get_determinant_for_string(type,line_number) # adding end-of-line str += "\n" str += "\n" return str def get_determinant_for_string(self, type, number): if number >= len(self.determinants[type]): empty_determinant = "%s%4d%2s" % ("XXX", 0, "X") return "%8.2lf %s" % (0.0, empty_determinant) else: determinant = self.determinants[type][number] return "%8.2lf %s" % (determinant.value, determinant.label) def calculate_total_pka(self): # if this is a cysteine involved in a di-sulphide bond if self.atom.cysteine_bridge: self.pka_value = 99.99 return self.pka_value = self.model_pka + self.Emass + self.Elocl for determinant_type in ['sidechain', 'backbone', 'coulomb']: for determinant in self.determinants[determinant_type]: self.pka_value += determinant.value return def calculate_intrinsic_pka(self): """ Calculates the intrinsic pKa values from the desolvation determinants, back-bone hydrogen bonds, and side-chain hydrogen bond to non-titratable residues """ back_bone = 0.0 for determinant in self.determinants['backbone']: value = determinant.value back_bone += value side_chain = 0.0 for determinant in self.determinants['sidechain']: if determinant.label[0:3] not in ['ASP','GLU','LYS','ARG','HIS','CYS','TYR','C- ','N+ ']: value = determinant.value side_chain += value self.intrinsic_pKa = self.model_pka + self.Emass + self.Elocl + back_bone + side_chain return def getSummaryString(self, remove_penalised_group=False): if self.coupled_titrating_group and remove_penalised_group: return '' ligand_type = '' if self.atom.type == 'hetatm': ligand_type = self.type penalty = '' if self.coupled_titrating_group: penalty = ' NB: Discarded due to coupling with %s'%self.coupled_titrating_group.label str = " %9s %8.2lf %10.2lf %18s %s\n" % (self.label, self.pka_value, self.model_pka,ligand_type, penalty) return str def __str__(self): return 'Group (%s) for %s'%(self.type,self.atom) # # Energy-related methods # def calculate_folding_energy(self, parameters, pH=None, reference=None): """ returning the electrostatic energy of this residue at pH 'pH' """ if pH == None: pH = parameters.pH if reference == None: reference = parameters.reference # If not titratable, the contribution is zero if not self.titratable: return 0.00 # calculating the ddG(neutral --> low-pH) contribution ddG_neutral = 0.00 if reference == 'neutral' and self.charge > 0.00: pka_prime = self.pka_value for determinant in self.determinants['coulomb']: if determinant.value > 0.00: pka_prime -= determinant.value ddG_neutral = -1.36*(pka_prime - self.model_pka) # calculating the ddG(low-pH --> pH) contribution # folded x = pH - self.pka_value y = 10**x Q_pro = math.log10(1+y) # unfolded x = pH - self.model_pka y = 10**x Q_mod = math.log10(1+y) ddG_low = -1.36*(Q_pro - Q_mod) ddG = ddG_neutral + ddG_low return ddG def calculate_charge(self, parmaeters, pH=7.0, state='folded'): if state == "unfolded": x = self.charge * (self.model_pka - pH) else: x = self.charge * (self.pka_value - pH) y = 10**x charge = self.charge*(y/(1.0+y)) return charge class COO_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'COO' def setup_atoms(self): # Identify the two caroxyl oxygen atoms the_oxygens = self.atom.get_bonded_elements('O') # set the center using the two oxygen carboxyl atoms self.set_center(the_oxygens) self.set_interaction_atoms(the_oxygens, the_oxygens) return class HIS_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'HIS' def setup_atoms(self): # Find the atoms in the histidine ring ring_atoms = Source.ligand.is_ring_member(self.atom) if len(ring_atoms) != 5: print('Warning: His group does not seem to contain a ring',self) # protonate ring for r in ring_atoms: my_protonator.protonate_atom(r) # set the center using the ring atoms self.set_center(ring_atoms) # find the hydrogens on the ring-nitrogens hydrogens = [] nitrogens = [ra for ra in ring_atoms if ra.element == 'N'] for nitrogen in nitrogens: hydrogens.extend(nitrogen.get_bonded_elements('H')) self.set_interaction_atoms(hydrogens+nitrogens, nitrogens) return class CYS_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'CYS' class TYR_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'TYR' class LYS_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'LYS' class ARG_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'ARG' def setup_atoms(self): # set the center at the position of the main atom self.set_center([self.atom]) # find all the hydrogens on the nitrogen atoms nitrogens = self.atom.get_bonded_elements('N') for n in nitrogens: my_protonator.protonate_atom(n) hydrogens = [] for nitrogen in nitrogens: hydrogens.extend(nitrogen.get_bonded_elements('H')) self.set_interaction_atoms(nitrogens+hydrogens, nitrogens) return class ROH_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'ROH' class SER_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'SER' class AMD_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'AMD' def setup_atoms(self): # Identify the oxygen and nitrogen amide atoms the_oxygen = self.atom.get_bonded_elements('O') the_nitrogen = self.atom.get_bonded_elements('N') # add protons to the nitrogen my_protonator.protonate_atom(the_nitrogen[0]) the_hydrogens = the_nitrogen[0].get_bonded_elements('H') # set the center using the oxygen and nitrogen amide atoms self.set_center(the_oxygen+the_nitrogen) self.set_interaction_atoms(the_nitrogen+the_hydrogens,the_oxygen) return class TRP_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'TRP' def setup_atoms(self): # set the center at the position of the main atom self.set_center([self.atom]) # find the hydrogen on the nitrogen atom my_protonator.protonate_atom(self.atom) the_hydrogen = self.atom.get_bonded_elements('H') self.set_interaction_atoms(the_hydrogen+[self.atom], [self.atom]) return class Nterm_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'N+' class Cterm_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'COO' # this is to deal with the COO-C- parameter unification. def setup_atoms(self): # Identify the carbon and other oxygen carboxyl atoms the_carbon = self.atom.get_bonded_elements('C') the_other_oxygen = the_carbon[0].get_bonded_elements('O') the_other_oxygen.remove(self.atom) # set the center and interaction atoms the_oxygens = [self.atom]+ the_other_oxygen self.set_center(the_oxygens) self.set_interaction_atoms(the_oxygens, the_oxygens) return class BBN_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'BBN' self.residue_type = 'BBN' def setup_atoms(self): # Identify the hydrogen my_protonator.protonate_atom(self.atom) the_hydrogen = self.atom.get_bonded_elements('H') # set the center using the nitrogen self.set_center([self.atom]) self.set_interaction_atoms(the_hydrogen+[self.atom], the_hydrogen+[self.atom]) return class BBC_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'BBC' self.residue_type = 'BBC' def setup_atoms(self): # Identify the oxygen the_oxygen = self.atom.get_bonded_elements('O') # set the center using the nitrogen self.set_center([self.atom]) self.set_interaction_atoms(the_oxygen, the_oxygen) return class NAR_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'NAR' self.residue_type = 'NAR' print('Found NAR group:',atom) return def setup_atoms(self): # Identify the hydrogen my_protonator.protonate_atom(self.atom) the_hydrogen = self.atom.get_bonded_elements('H') # set the center using the nitrogen self.set_center([self.atom]) self.set_interaction_atoms(the_hydrogen+[self.atom], the_hydrogen+[self.atom]) return class NAM_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'NAM' self.residue_type = 'NAM' print('Found NAM group:',atom) return def setup_atoms(self): # Identify the hydrogen my_protonator.protonate_atom(self.atom) the_hydrogen = self.atom.get_bonded_elements('H') # set the center using the nitrogen self.set_center([self.atom]) self.set_interaction_atoms(the_hydrogen+[self.atom], the_hydrogen+[self.atom]) return class F_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'F' self.residue_type = 'F' print('Found F group:',atom) return class Cl_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'Cl' self.residue_type = 'Cl' print('Found Cl group:',atom) return class OH_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'OH' self.residue_type = 'OH' print('Found OH group:',atom) return def setup_atoms(self): # Identify the hydrogen my_protonator.protonate_atom(self.atom) the_hydrogen = self.atom.get_bonded_elements('H') # set the center using the nitrogen self.set_center([self.atom]) self.set_interaction_atoms(the_hydrogen+[self.atom], the_hydrogen+[self.atom]) return class OP_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'OP' self.residue_type = 'OP' print('Found OP group:',atom) return def setup_atoms(self): # Identify the hydrogen my_protonator.protonate_atom(self.atom) #the_hydrogen = self.atom.get_bonded_elements('H') # set the center using the oxygen self.set_center([self.atom]) #self.set_interaction_atoms(the_hydrogen+[self.atom], the_hydrogen+[self.atom]) self.set_interaction_atoms([self.atom], [self.atom]) return class O3_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'O3' self.residue_type = 'O3' print('Found O3 group:',atom) return class O2_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'O2' self.residue_type = 'O2' print('Found O2 group:',atom) return class SH_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'SH' self.residue_type = 'SH' print('Found SH group:',atom) return class CG_group(Group): """Guadinium group""" def __init__(self, atom): Group.__init__(self,atom) self.type = 'CG' self.residue_type = 'CG' print('Found CG group:',atom) return def setup_atoms(self): # Identify the nitrogens the_nitrogens = self.atom.get_bonded_elements('N') # set the center using the nitrogen self.set_center([self.atom]) the_hydrogens = [] for n in the_nitrogens: my_protonator.protonate_atom(n) the_hydrogens += n.get_bonded_elements('H') self.set_interaction_atoms(the_hydrogens+the_nitrogens, the_nitrogens) return class C2N_group(Group): """Amidinium group""" def __init__(self, atom): Group.__init__(self,atom) self.type = 'C2N' self.residue_type = 'C2N' print('Found C2N group:',atom) return def setup_atoms(self): # Identify the nitrogens the_nitrogens = self.atom.get_bonded_elements('N') the_nitrogens = [n for n in the_nitrogens if len(n.get_bonded_heavy_atoms())==1] # set the center using the nitrogen self.set_center([self.atom]) the_hydrogens = [] for n in the_nitrogens: my_protonator.protonate_atom(n) the_hydrogens += n.get_bonded_elements('H') self.set_interaction_atoms(the_hydrogens+the_nitrogens, the_nitrogens) return class OCO_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'OCO' self.residue_type = 'OCO' print('Found OCO group:',atom) return def setup_atoms(self): # Identify the two caroxyl oxygen atoms the_oxygens = self.atom.get_bonded_elements('O') # set the center using the two oxygen carboxyl atoms self.set_center(the_oxygens) self.set_interaction_atoms(the_oxygens, the_oxygens) return class N30_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'N30' self.residue_type = 'N30' print('Found N30 group:',atom) return def setup_atoms(self): # Identify the nitrogens my_protonator.protonate_atom(self.atom) the_hydrogens = self.atom.get_bonded_elements('H') # set the center using the nitrogen self.set_center([self.atom]) self.set_interaction_atoms(the_hydrogens+[self.atom], [self.atom]) return class N31_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'N31' self.residue_type = 'N31' print('Found N31 group:',atom) return def setup_atoms(self): # Identify the nitrogens my_protonator.protonate_atom(self.atom) the_hydrogens = self.atom.get_bonded_elements('H') # set the center using the nitrogen self.set_center([self.atom]) self.set_interaction_atoms(the_hydrogens+[self.atom], [self.atom]) return class N32_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'N32' self.residue_type = 'N32' print('Found N32 group:',atom) return def setup_atoms(self): # Identify the nitrogens my_protonator.protonate_atom(self.atom) the_hydrogens = self.atom.get_bonded_elements('H') # set the center using the nitrogen self.set_center([self.atom]) self.set_interaction_atoms(the_hydrogens+[self.atom], [self.atom]) return class N33_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'N33' self.residue_type = 'N33' print('Found N33 group:',atom) return def setup_atoms(self): # Identify the nitrogens my_protonator.protonate_atom(self.atom) the_hydrogens = self.atom.get_bonded_elements('H') # set the center using the nitrogen self.set_center([self.atom]) self.set_interaction_atoms(the_hydrogens+[self.atom], [self.atom]) return class NP1_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'NP1' self.residue_type = 'NP1' print('Found NP1 group:',atom) return def setup_atoms(self): # Identify the nitrogens my_protonator.protonate_atom(self.atom) the_hydrogens = self.atom.get_bonded_elements('H') # set the center using the nitrogen self.set_center([self.atom]) self.set_interaction_atoms(the_hydrogens+[self.atom], [self.atom]) return class N1_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'N1' self.residue_type = 'N1' print('Found N1 group:',atom) return class Ion_group(Group): def __init__(self, atom): Group.__init__(self,atom) self.type = 'ION' self.residue_type = atom.resName.strip() print('Found ion group:',atom) return class non_titratable_ligand_group(Group): def __init__(self, atom): Group.__init__(self, atom) self.type = 'LG' self.residue_type = 'LG' # print('Non-titratable ligand group',atom) return class titratable_ligand_group(Group): def __init__(self, atom): Group.__init__(self, atom) # set the charge and determine type (acid or base) self.charge = atom.charge if self.charge <0: self.type = 'ALG' self.residue_type = 'ALG' elif self.charge > 0: self.type = 'BLG' self.residue_type = 'BLG' else: raise Exception('Unable to determine type of ligand group - charge not set?') # check if marvin model pka has been calculated # this is not true if we are reading an input file if atom.marvin_pka: self.model_pka = atom.marvin_pka print('Titratable ligand group ',atom, self.model_pka, self.charge) self.model_pka_set = True return def is_group(parameters, atom): atom.groups_extracted = 1 # check if this atom belongs to a protein group protein_group = is_protein_group(parameters, atom) if protein_group: return protein_group # check if this atom belongs to a ion group ion_group = is_ion_group(parameters, atom) if ion_group: return ion_group # check if this atom belongs to a ligand group if parameters.ligand_typing == 'marvin': ligand_group = is_ligand_group_by_marvin_pkas(parameters, atom) elif parameters.ligand_typing == 'sybyl': ligand_group = is_ligand_group_by_sybyl_types(parameters, atom) elif parameters.ligand_typing == 'groups': ligand_group = is_ligand_group_by_groups(parameters, atom) else: raise Exception('Unknown ligand typing method \'%s\''%parameters.ligand_typing) if ligand_group: return ligand_group return None def is_protein_group(parameters,atom): if atom.type != 'atom': return None ### Check for termial groups if atom.terminal == 'N+': return Nterm_group(atom) elif atom.terminal == 'C-': return Cterm_group(atom) ### Backbone if atom.type == 'atom' and atom.name == 'N': # ignore proline backbone nitrogens if atom.resName != 'PRO': return BBN_group(atom) if atom.type == 'atom' and atom.name == 'C': # ignore C- carboxyl if atom.count_bonded_elements('O') == 1: return BBC_group(atom) ### Filters for side chains based on PDB protein atom names key = '%s-%s'%(atom.resName, atom.name) if key in parameters.protein_group_mapping.keys(): return eval('%s_group(atom)'%parameters.protein_group_mapping[key]) return None def is_ligand_group_by_sybyl_types(parameters, atom): return None def is_ligand_group_by_groups(parameters, atom): ### Ligand group filters if atom.type != 'hetatm': return None my_protonator.protonate_atom(atom) if atom.sybyl_type == 'N.ar': if len(atom.get_bonded_heavy_atoms())==2: return NAR_group(atom) if atom.sybyl_type == 'N.am': return NAM_group(atom) if atom.sybyl_type in ['N.3', 'N.4']: heavy_bonded = atom.get_bonded_heavy_atoms() if len(heavy_bonded) == 0: return N30_group(atom) elif len(heavy_bonded) == 1: return N31_group(atom) elif len(heavy_bonded) == 2: return N32_group(atom) elif len(heavy_bonded) == 3: return N33_group(atom) if atom.sybyl_type == 'N.1': return N1_group(atom) if atom.sybyl_type == 'N.pl3': # make sure that this atom is not part of a guadinium or amidinium group bonded_carbons = atom.get_bonded_elements('C') if len(bonded_carbons) == 1: bonded_nitrogens = bonded_carbons[0].get_bonded_elements('N') if len(bonded_nitrogens) == 1: return NP1_group(atom) if atom.sybyl_type == 'C.2': # Guadinium and amidinium groups bonded_nitrogens = atom.get_bonded_elements('N') npls = [n for n in bonded_nitrogens if (n.sybyl_type == 'N.pl3' and len(n.get_bonded_heavy_atoms())==1)] if len(npls) == 2: n_with_max_two_heavy_atom_bonds = [n for n in bonded_nitrogens if len(n.get_bonded_heavy_atoms())<3] if len(n_with_max_two_heavy_atom_bonds) == 2: return C2N_group(atom) if len(bonded_nitrogens) == 3: return CG_group(atom) # carboxyl group bonded_oxygens = atom.get_bonded_elements('O') bonded_oxygens = [b for b in bonded_oxygens if 'O.co2' in b.sybyl_type] if len(bonded_oxygens) == 2: return OCO_group(atom) if atom.sybyl_type == 'F': return F_group(atom) if atom.sybyl_type == 'Cl': return Cl_group(atom) if atom.sybyl_type == 'O.3': if len(atom.get_bonded_heavy_atoms()) == 1: # phosphate group if atom.count_bonded_elements('P') == 1: return OP_group(atom) # hydroxyl group else: return OH_group(atom) # other SP3 oxygen else: return O3_group(atom) if atom.sybyl_type == 'O.2': return O2_group(atom) if atom.sybyl_type == 'S.3': # sulphide group if len(atom.get_bonded_heavy_atoms()) == 1: return SH_group(atom) # other SP3 oxygen #else: # return S3_group(atom) return None def is_ligand_group_by_marvin_pkas(parameters, atom): if atom.type != 'hetatm': return None # calculate Marvin ligand pkas for this conformation container # if not already done if not atom.conformation_container.marvin_pkas_calculated: lpka = Source.ligand_pka_values.ligand_pka_values(parameters) lpka.get_marvin_pkas_for_molecular_container(atom.molecular_container, min_pH=parameters.min_ligand_model_pka, max_pH=parameters.max_ligand_model_pka) if atom.marvin_pka: return titratable_ligand_group(atom) # Special case of oxygen in carboxyl group not assigned a pka value by marvin if atom.sybyl_type == 'O.co2': atom.charge = -1.0 other_oxygen = [o for o in atom.get_bonded_elements('C')[0].get_bonded_elements('O') if not o==atom][0] atom.marvin_pka = other_oxygen.marvin_pka return titratable_ligand_group(atom) if atom.element in parameters.hydrogen_bonds.elements: return non_titratable_ligand_group(atom) return None def is_ion_group(parameters, atom): if atom.resName.strip() in parameters.ions.keys(): return Ion_group(atom) return None