stcrpy 1.0.0__py3-none-any.whl → 1.0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- stcrpy/__init__.py +1 -1
- stcrpy/tcr_formats/tcr_formats.py +20 -1
- stcrpy/tcr_geometry/TCRAngle.py +177 -0
- stcrpy/tcr_geometry/TCRDock.py +4 -1
- stcrpy/tcr_geometry/reference_data/Acoreset.txt +30 -0
- stcrpy/tcr_geometry/reference_data/Bcoreset.txt +30 -0
- stcrpy/tcr_geometry/reference_data/consensus_A.pdb +31 -0
- stcrpy/tcr_geometry/reference_data/consensus_B.pdb +31 -0
- stcrpy/tcr_geometry/reference_data/consensus_D.pdb +31 -0
- stcrpy/tcr_geometry/reference_data/consensus_G.pdb +31 -0
- stcrpy/tcr_geometry/reference_data/pcA.txt +3 -0
- stcrpy/tcr_geometry/reference_data/pcB.txt +3 -0
- stcrpy/tcr_interactions/TCRInteractionProfiler.py +1 -1
- stcrpy/tcr_interactions/TCRpMHC_PLIP_Model_Parser.py +21 -0
- stcrpy/tcr_methods/tcr_batch_operations.py +14 -10
- stcrpy/tcr_methods/tcr_methods.py +23 -22
- stcrpy/tcr_metrics/tcr_dockq.py +404 -0
- stcrpy/tcr_processing/Chemical_components.py +4 -4
- stcrpy/tcr_processing/Entity.py +15 -16
- stcrpy/tcr_processing/MHC.py +456 -4
- stcrpy/tcr_processing/TCR.py +462 -14
- stcrpy/tcr_processing/TCRParser.py +364 -193
- stcrpy/tcr_processing/annotate.py +35 -24
- stcrpy/tcr_processing/utils/common.py +3 -2
- stcrpy/tcr_processing/utils/constants.py +4 -3
- stcrpy/tcr_processing/utils/region_definitions.py +9 -0
- stcrpy/tcr_processing/utils/symmetry_mates.py +90 -0
- stcrpy-1.0.5.dist-info/METADATA +285 -0
- {stcrpy-1.0.0.dist-info → stcrpy-1.0.5.dist-info}/RECORD +33 -22
- {stcrpy-1.0.0.dist-info → stcrpy-1.0.5.dist-info}/WHEEL +1 -1
- stcrpy-1.0.0.dist-info/METADATA +0 -173
- {stcrpy-1.0.0.dist-info → stcrpy-1.0.5.dist-info}/licenses/LICENCE +0 -0
- {stcrpy-1.0.0.dist-info → stcrpy-1.0.5.dist-info}/licenses/stcrpy/tcr_geometry/TCRCoM_LICENCE +0 -0
- {stcrpy-1.0.0.dist-info → stcrpy-1.0.5.dist-info}/top_level.txt +0 -0
stcrpy/tcr_processing/TCR.py
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Created on 3rd April 2024
|
|
3
|
-
|
|
3
|
+
Nele Quast based on work by Dunbar and Leem
|
|
4
4
|
The TCR class.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import sys
|
|
8
8
|
import warnings
|
|
9
9
|
|
|
10
|
+
from Bio import BiopythonWarning
|
|
11
|
+
|
|
10
12
|
from .Entity import Entity
|
|
13
|
+
from .TCRchain import TCRchain
|
|
14
|
+
from .utils.region_definitions import IMGT_VARIABLE_DOMAIN
|
|
11
15
|
|
|
12
16
|
try:
|
|
13
17
|
from .. import tcr_interactions
|
|
@@ -18,17 +22,34 @@ except ImportError as e:
|
|
|
18
22
|
print(e)
|
|
19
23
|
|
|
20
24
|
|
|
25
|
+
class TCRError(Exception):
|
|
26
|
+
"""Error raised when there is an issue with the TCR."""
|
|
27
|
+
|
|
28
|
+
|
|
21
29
|
class TCR(Entity):
|
|
22
30
|
"""
|
|
23
|
-
TCR class.
|
|
24
|
-
|
|
31
|
+
TCR class. Inherits from PDB.Entity.
|
|
32
|
+
This is a base class for TCR strucutres, enabling antigen and MHC association.
|
|
33
|
+
abTCR and gdTCR are the instantiated subclasses of this class.
|
|
25
34
|
"""
|
|
26
35
|
|
|
27
36
|
def _add_antigen(self, antigen=None):
|
|
37
|
+
"""
|
|
38
|
+
Append associated antigen to TCR antigen field.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
antigen (Antigen, optional): Antigen to associate with TCR. Defaults to None.
|
|
42
|
+
"""
|
|
28
43
|
if antigen not in self.antigen:
|
|
29
44
|
self.antigen.append(antigen)
|
|
30
45
|
|
|
31
46
|
def _add_mhc(self, mhc=None):
|
|
47
|
+
"""
|
|
48
|
+
Append associated MHC to TCR MHC field. If antigen are associted with MHC but not TCR, add them to TCR antigen.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
mhc (MHC, optional): MHC to associate with TCR. Defaults to None.
|
|
52
|
+
"""
|
|
32
53
|
if mhc not in self.MHC:
|
|
33
54
|
self.MHC.append(mhc)
|
|
34
55
|
# If there are any het antigens that are in the MHC but not in close proximity of the TCR
|
|
@@ -36,19 +57,41 @@ class TCR(Entity):
|
|
|
36
57
|
if set(mhc.antigen) - set(self.antigen):
|
|
37
58
|
self.antigen.extend(mhc.antigen)
|
|
38
59
|
|
|
60
|
+
def copy(self, copy_siblings = True ):
|
|
61
|
+
"""
|
|
62
|
+
Return a copy of the TCR object. This returns a shallow copy of the TCR object.
|
|
63
|
+
If the copy_siblings flag is set to True, the antigen and MHC objects will also be copied. Warning - if the copy_siblings flag is set to False, the antigen and MHC objects will not be copied, and the reference will still point to the same MHC and antigen objects as the original.
|
|
64
|
+
|
|
65
|
+
copy_siblings: Whether to copy sibling entities (ie. MHC and Antigen objects). Default True.
|
|
66
|
+
|
|
67
|
+
"""
|
|
68
|
+
shallow = super().copy()
|
|
69
|
+
if copy_siblings:
|
|
70
|
+
shallow.antigen = [a.copy() for a in self.get_antigen()]
|
|
71
|
+
shallow.MHC = [m.copy(copy_siblings=False) for m in self.get_MHC()]
|
|
72
|
+
for m in shallow.MHC:
|
|
73
|
+
m.tcr = [t.copy(copy_siblings=False) if t.id != shallow.id else shallow for t in m.tcr]
|
|
74
|
+
m.antigen = [ag.copy() if ag.id not in [a.id for a in shallow.antigen] else [a for a in shallow.antigen if a.id==ag.id][0] for ag in m.antigen]
|
|
75
|
+
|
|
76
|
+
return shallow
|
|
77
|
+
|
|
39
78
|
def get_antigen(self):
|
|
40
79
|
"""
|
|
41
|
-
Return a list of
|
|
80
|
+
Return a list of TCR associated antigens.
|
|
42
81
|
"""
|
|
43
82
|
return self.antigen
|
|
44
83
|
|
|
45
84
|
def get_MHC(self):
|
|
46
|
-
"""
|
|
85
|
+
"""
|
|
86
|
+
Return a list of TCR associated MHCs.
|
|
87
|
+
"""
|
|
47
88
|
return self.MHC
|
|
48
89
|
|
|
49
90
|
def is_bound(self):
|
|
50
|
-
"""
|
|
51
|
-
|
|
91
|
+
"""True or False if the TCR is associated with an antigen.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
bool: Whether TCR is associated with an antigen.
|
|
52
95
|
"""
|
|
53
96
|
if self.get_antigen():
|
|
54
97
|
return True
|
|
@@ -56,22 +99,40 @@ class TCR(Entity):
|
|
|
56
99
|
return False
|
|
57
100
|
|
|
58
101
|
def get_chains(self):
|
|
102
|
+
"""Returns generator of TCR chains.
|
|
103
|
+
|
|
104
|
+
Yields:
|
|
105
|
+
Chain: TCR chain
|
|
106
|
+
"""
|
|
59
107
|
for c in self:
|
|
60
108
|
yield c
|
|
61
109
|
|
|
62
110
|
def get_residues(self):
|
|
111
|
+
"""Returns generator of TCR residues.
|
|
112
|
+
|
|
113
|
+
Yields:
|
|
114
|
+
Residue: TCR residue
|
|
115
|
+
"""
|
|
63
116
|
for c in self.get_chains():
|
|
64
117
|
for r in c:
|
|
65
118
|
yield r
|
|
66
119
|
|
|
67
120
|
def get_atoms(self):
|
|
121
|
+
"""Returns generator of TCR atoms.
|
|
122
|
+
|
|
123
|
+
Yields:
|
|
124
|
+
Atom: TCR atoms
|
|
125
|
+
"""
|
|
68
126
|
for r in self.get_residues():
|
|
69
127
|
for a in r:
|
|
70
128
|
yield a
|
|
71
129
|
|
|
72
130
|
def get_frameworks(self):
|
|
73
131
|
"""
|
|
74
|
-
Obtain framework regions from a TCR structure object.
|
|
132
|
+
Obtain framework regions from a TCR structure object as generator.
|
|
133
|
+
|
|
134
|
+
Yields:
|
|
135
|
+
Fragment: TCR framework regions
|
|
75
136
|
"""
|
|
76
137
|
for f in self.get_fragments():
|
|
77
138
|
if "fw" in f.id:
|
|
@@ -79,15 +140,20 @@ class TCR(Entity):
|
|
|
79
140
|
|
|
80
141
|
def get_CDRs(self):
|
|
81
142
|
"""
|
|
82
|
-
Obtain
|
|
143
|
+
Obtain complementarity determining regions (CDRs) from a TCR structure object as generator.
|
|
144
|
+
|
|
145
|
+
Yields:
|
|
146
|
+
Fragment: TCR CDR regions
|
|
83
147
|
"""
|
|
84
148
|
for f in self.get_fragments():
|
|
85
149
|
if "cdr" in f.id:
|
|
86
150
|
yield f
|
|
87
151
|
|
|
88
152
|
def get_TCR_type(self):
|
|
89
|
-
"""
|
|
90
|
-
|
|
153
|
+
"""Get TCR type according to variable region assignments.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
str: TCR type (abTCR, gdTCR, dbTCR)
|
|
91
157
|
"""
|
|
92
158
|
if hasattr(self, "tcr_type"):
|
|
93
159
|
return self.tcr_type
|
|
@@ -102,9 +168,22 @@ class TCR(Entity):
|
|
|
102
168
|
return self.tcr_type
|
|
103
169
|
|
|
104
170
|
def get_germline_assignments(self):
|
|
171
|
+
"""Retrive germline assignments for all TCR chains.
|
|
172
|
+
This is a dictionary with the chain ID as key and the germline assignments as value.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
dict: dict with TCR chain ID as key and germline assignments as value
|
|
176
|
+
"""
|
|
105
177
|
return {c.id: c.get_germline_assignments() for c in self.get_chains()}
|
|
106
178
|
|
|
107
179
|
def get_MHC_allele_assignments(self):
|
|
180
|
+
"""
|
|
181
|
+
Retrieve MHC allele assignments for all TCR associated MHCs.
|
|
182
|
+
This is a list of dictionaries with the MHC ID as key and the allele assignments as value.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
dict: dict with MHC chain ID as key and allele assignments as value
|
|
186
|
+
"""
|
|
108
187
|
return [
|
|
109
188
|
(
|
|
110
189
|
mhc.get_allele_assignments()
|
|
@@ -116,6 +195,11 @@ class TCR(Entity):
|
|
|
116
195
|
]
|
|
117
196
|
|
|
118
197
|
def get_germlines_and_alleles(self):
|
|
198
|
+
"""Get all germline and allele assignments for TCR and MHC chains as a dictionary with the chain ID as key and the germline assignments as value.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
dict: Dictionary of TCR germline and MHC allele assignemnts with amino acid sequences.
|
|
202
|
+
"""
|
|
119
203
|
from ..tcr_formats.tcr_formats import get_sequences
|
|
120
204
|
|
|
121
205
|
germlines_and_alleles = {}
|
|
@@ -166,23 +250,79 @@ class TCR(Entity):
|
|
|
166
250
|
|
|
167
251
|
return germlines_and_alleles
|
|
168
252
|
|
|
253
|
+
def get_chain_mapping(self):
|
|
254
|
+
"""Get a dictionary of chain IDs to chain types.
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
dict: Dictionary of chain IDs to chain types
|
|
258
|
+
"""
|
|
259
|
+
tcr_chain_mapping = {v: k for k, v in self.get_domain_assignment().items()}
|
|
260
|
+
antigen_chain_mapping = {c.id: "Ag" for c in self.get_antigen()}
|
|
261
|
+
mhc_chain_mapping = {
|
|
262
|
+
c.id: c.chain_type for m in self.get_MHC() for c in m.get_chains()
|
|
263
|
+
}
|
|
264
|
+
chain_mapping = {
|
|
265
|
+
**tcr_chain_mapping,
|
|
266
|
+
**antigen_chain_mapping,
|
|
267
|
+
**mhc_chain_mapping,
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return chain_mapping
|
|
271
|
+
|
|
169
272
|
def save(self, save_as=None, tcr_only: bool = False, format: str = "pdb"):
|
|
273
|
+
"""Save TCR object as PDB or MMCIF file.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
save_as (str, optional): File path to save TCR to. Defaults to None.
|
|
277
|
+
tcr_only (bool, optional): Whether to save TCR only or to include MHC and antigen. Defaults to False.
|
|
278
|
+
format (str, optional): Whether to save as PDB or MMCIF. Defaults to "pdb".
|
|
279
|
+
"""
|
|
170
280
|
from . import TCRIO
|
|
171
281
|
|
|
172
282
|
tcrio = TCRIO.TCRIO()
|
|
173
283
|
tcrio.save(self, save_as=save_as, tcr_only=tcr_only, format=format)
|
|
174
284
|
|
|
175
285
|
def get_scanning_angle(self, mode="rudolph"):
|
|
286
|
+
"""
|
|
287
|
+
Returns TCR:pMHC complex scanning (aka crossing, incident) angle of TCR to MHC.
|
|
288
|
+
See paper for details.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
mode (str, optional): Mode for calculating the scanning angle. Options "rudolph", "cys", "com". Defaults to "rudolph".
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
float: Scanning angle of TCR to MHC in degrees
|
|
295
|
+
"""
|
|
176
296
|
if not hasattr(self, "geometry") or self.geometry.mode != mode:
|
|
177
297
|
self.calculate_docking_geometry(mode=mode)
|
|
178
298
|
return self.geometry.get_scanning_angle()
|
|
179
299
|
|
|
180
|
-
def get_pitch_angle(self, mode="
|
|
300
|
+
def get_pitch_angle(self, mode="cys"):
|
|
301
|
+
"""
|
|
302
|
+
Returns TCR:pMHC complex pitch angle of TCR to MHC.
|
|
303
|
+
See paper for details.
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
mode (str, optional): Mode for calculating the scanning angle. Options "rudolph", "cys", "com". Defaults to "cys".
|
|
307
|
+
|
|
308
|
+
Returns:
|
|
309
|
+
float: Pitch angle of TCR to MHC in degrees
|
|
310
|
+
"""
|
|
181
311
|
if not hasattr(self, "geometry") or self.geometry.mode != mode:
|
|
182
312
|
self.calculate_docking_geometry(mode=mode)
|
|
183
313
|
return self.geometry.get_pitch_angle()
|
|
184
314
|
|
|
185
315
|
def calculate_docking_geometry(self, mode="rudolph", as_df=False):
|
|
316
|
+
"""Calculate docking geometry of TCR to MHC.
|
|
317
|
+
This is a wrapper function for the TCRGeom class.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
mode (str, optional): Mode for calculating the geometry. Options "rudolph", "cys", "com". Defaults to "rudolph".
|
|
321
|
+
as_df (bool, optional): Whether to return as dictionary or dataframe. Defaults to False.
|
|
322
|
+
|
|
323
|
+
Returns:
|
|
324
|
+
[dict, DataFrame]: TCR to MHC geometry.
|
|
325
|
+
"""
|
|
186
326
|
if len(self.get_MHC()) == 0:
|
|
187
327
|
warnings.warn(
|
|
188
328
|
f"No MHC found for TCR {self}. Docking geometry cannot be calcuated"
|
|
@@ -203,20 +343,39 @@ class TCR(Entity):
|
|
|
203
343
|
return self.geometry.to_dict()
|
|
204
344
|
|
|
205
345
|
def score_docking_geometry(self, **kwargs):
|
|
346
|
+
"""
|
|
347
|
+
Score docking geometry of TCR to MHC.
|
|
348
|
+
This is a wrapper function for the TCRGeomFiltering class.
|
|
349
|
+
The score is calculated as the negative log of the TCR:pMHC complex geometry feature probabilities based on the distributions fit by maximum likelihood estimation of TCR to Class I MHC strucutres from STCRDab.
|
|
350
|
+
Please see the paper methods for details.
|
|
351
|
+
|
|
352
|
+
Returns:
|
|
353
|
+
float: TCR:pMHC complex score as negative log of TCR:pMHC complex geometry feature probabilities
|
|
354
|
+
"""
|
|
206
355
|
from ..tcr_geometry.TCRGeomFiltering import DockingGeometryFilter
|
|
207
356
|
|
|
208
357
|
geom_filter = DockingGeometryFilter()
|
|
209
358
|
if not hasattr(self, "geometry"):
|
|
210
359
|
self.calculate_docking_geometry(mode="com")
|
|
211
360
|
return geom_filter.score_docking_geometry(
|
|
212
|
-
self.geometry.get_scanning_angle(),
|
|
213
|
-
self.geometry.get_pitch_angle(),
|
|
361
|
+
self.geometry.get_scanning_angle(mode="com"),
|
|
362
|
+
self.geometry.get_pitch_angle(mode="com"),
|
|
214
363
|
self.geometry.tcr_com[-1], # z component of TCR centre of mass
|
|
215
364
|
)
|
|
216
365
|
|
|
217
366
|
def profile_peptide_interactions(
|
|
218
367
|
self, renumber: bool = True, save_to: str = None, **kwargs
|
|
219
368
|
) -> "pd.DataFrame":
|
|
369
|
+
"""
|
|
370
|
+
Profile the interactions of the peptide to the TCR and the MHC.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
renumber (bool, optional): Whether to renumber the interacting residues. Defaults to True.
|
|
374
|
+
save_to (str, optional): Path to save intraction data to as csv. Defaults to None.
|
|
375
|
+
|
|
376
|
+
Returns:
|
|
377
|
+
pd.DataFrame: Dataframe of peptide interactions
|
|
378
|
+
"""
|
|
220
379
|
if len(self.get_antigen()) == 0:
|
|
221
380
|
warnings.warn(
|
|
222
381
|
f"No peptide antigen found for TCR {self}. Peptide interactions cannot be profiled"
|
|
@@ -238,6 +397,25 @@ class TCR(Entity):
|
|
|
238
397
|
return interactions
|
|
239
398
|
|
|
240
399
|
def get_interaction_heatmap(self, plotting_kwargs={}, **interaction_kwargs):
|
|
400
|
+
"""
|
|
401
|
+
Get interaction heatmap of TCR to MHC and peptide.
|
|
402
|
+
Generates heatmap image.
|
|
403
|
+
Plotting kwargs are passed to heatmap function.
|
|
404
|
+
|
|
405
|
+
Args:
|
|
406
|
+
plotting_kwargs (dict, optional):
|
|
407
|
+
save_as: path to save heatmap image to
|
|
408
|
+
interaction_type: type of interaction (eg. saltbridge, h_bond) to plot. All interactions are plotted by default.
|
|
409
|
+
antigen_name: name of antigen for plot title
|
|
410
|
+
mutation_index: index of antigen residues to highlight in plot
|
|
411
|
+
Defaults to {
|
|
412
|
+
save_as:None,
|
|
413
|
+
interaction_type:None,
|
|
414
|
+
antigen_name:None,
|
|
415
|
+
mutation_index:None
|
|
416
|
+
}.
|
|
417
|
+
interaction_kwargs: kwargs for TCRInteractionProfiler class. See TCRInteractionProfiler for details.
|
|
418
|
+
"""
|
|
241
419
|
from ..tcr_interactions import TCRInteractionProfiler
|
|
242
420
|
|
|
243
421
|
interaction_profiler = TCRInteractionProfiler.TCRInteractionProfiler(
|
|
@@ -251,6 +429,44 @@ class TCR(Entity):
|
|
|
251
429
|
def profile_MHC_interactions(self):
|
|
252
430
|
raise NotImplementedError
|
|
253
431
|
|
|
432
|
+
def get_TCR_angles(self):
|
|
433
|
+
from ..tcr_geometry.TCRAngle import TCRAngle
|
|
434
|
+
|
|
435
|
+
return TCRAngle().calculate_angles(self)
|
|
436
|
+
|
|
437
|
+
def crop(self, *, crop_mhc: bool = True, remove_het_atoms: bool = True) -> None:
|
|
438
|
+
"""Crop TCR to variable domain and optionally crop MHC to antigen binding domain.
|
|
439
|
+
|
|
440
|
+
This method mutates the TCR object.
|
|
441
|
+
|
|
442
|
+
Args:
|
|
443
|
+
crop_mhc: crop mhc to antigen binding domain
|
|
444
|
+
remove_het_atoms: remove het atoms from structure as well
|
|
445
|
+
|
|
446
|
+
"""
|
|
447
|
+
new_child_dict = {}
|
|
448
|
+
for chain in self:
|
|
449
|
+
new_chain = TCRchain(chain.id)
|
|
450
|
+
|
|
451
|
+
for residue in chain:
|
|
452
|
+
if residue.id[1] in IMGT_VARIABLE_DOMAIN or (not remove_het_atoms and residue.id[0] != ' '):
|
|
453
|
+
new_chain.add(residue.copy())
|
|
454
|
+
|
|
455
|
+
new_chain.analyse(chain.chain_type)
|
|
456
|
+
new_chain.set_engineered(chain.engineered)
|
|
457
|
+
new_chain.xtra.update(chain.xtra)
|
|
458
|
+
new_child_dict[new_chain.id] = new_chain
|
|
459
|
+
|
|
460
|
+
for chain_id in new_child_dict:
|
|
461
|
+
del self[chain_id]
|
|
462
|
+
|
|
463
|
+
for new_chain in new_child_dict.values():
|
|
464
|
+
self.add(new_chain)
|
|
465
|
+
|
|
466
|
+
if crop_mhc:
|
|
467
|
+
for mhc in self.get_MHC():
|
|
468
|
+
mhc.crop(remove_het_atoms=remove_het_atoms)
|
|
469
|
+
|
|
254
470
|
def _create_interaction_visualiser(self):
|
|
255
471
|
"""Function called during TCR initialisation checks if pymol is installed and assigns a visualisation method accordingly.
|
|
256
472
|
If pymol is installed, method to generate interaction visualisations is returned.
|
|
@@ -266,6 +482,15 @@ class TCR(Entity):
|
|
|
266
482
|
def visualise_interactions(
|
|
267
483
|
save_as=None, antigen_residues_to_highlight=None, **interaction_kwargs
|
|
268
484
|
):
|
|
485
|
+
"""Visualise peptide interactions in pymol.
|
|
486
|
+
|
|
487
|
+
Args:
|
|
488
|
+
save_as (str, optional): path to save pymol session to. Defaults to None.
|
|
489
|
+
antigen_residues_to_highlight (list[int], optional): antigen residues to highlight red in pymol session. Defaults to None.
|
|
490
|
+
**interaction_kwargs: kwargs for TCRInteractionProfiler class. See TCRInteractionProfiler for details.
|
|
491
|
+
Returns:
|
|
492
|
+
str: path to saved pymol session
|
|
493
|
+
"""
|
|
269
494
|
from ..tcr_interactions import TCRInteractionProfiler
|
|
270
495
|
|
|
271
496
|
interaction_profiler = TCRInteractionProfiler.TCRInteractionProfiler(
|
|
@@ -304,9 +529,39 @@ class TCR(Entity):
|
|
|
304
529
|
|
|
305
530
|
return visualise_interactions
|
|
306
531
|
|
|
532
|
+
def standardise_chain_names(self):
|
|
533
|
+
"""Raises NotImplementedError."""
|
|
534
|
+
raise NotImplementedError()
|
|
535
|
+
|
|
536
|
+
def _validate_chain_standardising(self) -> None:
|
|
537
|
+
if (hasattr(self, 'antigen') and len(self.antigen) > 1) or (hasattr(self, 'MHC') and len(self.MHC) > 1):
|
|
538
|
+
msg = 'More than one antigen or MHC molecule is not currently supported for standardising.'
|
|
539
|
+
raise TCRError(msg)
|
|
540
|
+
|
|
541
|
+
def _standardise_antigen_chain_names(self) -> None:
|
|
542
|
+
"""Will give the antigen the chain id C. Does not support multiple antigens."""
|
|
543
|
+
with warnings.catch_warnings():
|
|
544
|
+
warnings.simplefilter('ignore', BiopythonWarning)
|
|
545
|
+
self.antigen[0].id = 'C'
|
|
546
|
+
|
|
547
|
+
def _standardise_mhc_chain_names(self) -> None:
|
|
548
|
+
"""Will give the MHC first chain id A and second chain B. Does not support more than one MHC molecule."""
|
|
549
|
+
self.MHC[0].standardise_chain_names()
|
|
550
|
+
|
|
307
551
|
|
|
308
552
|
class abTCR(TCR):
|
|
553
|
+
"""
|
|
554
|
+
abTCR class. Inherits from TCR.
|
|
555
|
+
This is a subclass of TCR for TCRs with alpha and beta chains.
|
|
556
|
+
"""
|
|
309
557
|
def __init__(self, c1, c2):
|
|
558
|
+
"""
|
|
559
|
+
Initialise abTCR object. This is a subclass of TCR for TCRs with alpha and beta chains.
|
|
560
|
+
|
|
561
|
+
Args:
|
|
562
|
+
c1 (TCRchain): alpha or beta type TCR chain
|
|
563
|
+
c2 (TCRchain): alpha or beta type TCR chain
|
|
564
|
+
"""
|
|
310
565
|
|
|
311
566
|
if c1.chain_type == "B":
|
|
312
567
|
Entity.__init__(self, c1.id + c2.id)
|
|
@@ -328,9 +583,22 @@ class abTCR(TCR):
|
|
|
328
583
|
self.visualise_interactions = self._create_interaction_visualiser()
|
|
329
584
|
|
|
330
585
|
def __repr__(self):
|
|
586
|
+
"""
|
|
587
|
+
String representation of the abTCR object.
|
|
588
|
+
|
|
589
|
+
Returns:
|
|
590
|
+
str: String representation of the abTCR objec
|
|
591
|
+
"""
|
|
331
592
|
return "<TCR %s%s beta=%s; alpha=%s>" % (self.VB, self.VA, self.VB, self.VA)
|
|
332
593
|
|
|
333
594
|
def _add_domain(self, chain):
|
|
595
|
+
"""
|
|
596
|
+
Add a variable alpha or variable beta domain to the TCR object.
|
|
597
|
+
Links the domain to the chain ID.
|
|
598
|
+
|
|
599
|
+
Args:
|
|
600
|
+
chain (TCRchain): TCR chain whose domain is being added.
|
|
601
|
+
"""
|
|
334
602
|
if chain.chain_type == "B":
|
|
335
603
|
self.VB = chain.id
|
|
336
604
|
elif chain.chain_type == "A" or chain.chain_type == "D":
|
|
@@ -340,14 +608,32 @@ class abTCR(TCR):
|
|
|
340
608
|
self.add(chain)
|
|
341
609
|
|
|
342
610
|
def get_VB(self):
|
|
611
|
+
"""
|
|
612
|
+
Retrieve the variable beta chain of the TCR
|
|
613
|
+
|
|
614
|
+
Returns:
|
|
615
|
+
TCRchain: VB chain
|
|
616
|
+
"""
|
|
343
617
|
if hasattr(self, "VB"):
|
|
344
618
|
return self.child_dict[self.VB]
|
|
345
619
|
|
|
346
620
|
def get_VA(self):
|
|
621
|
+
"""
|
|
622
|
+
Retrieve the variable alpha chain of the TCR
|
|
623
|
+
|
|
624
|
+
Returns:
|
|
625
|
+
TCRchain: VA chain
|
|
626
|
+
"""
|
|
347
627
|
if hasattr(self, "VA"):
|
|
348
628
|
return self.child_dict[self.VA]
|
|
349
629
|
|
|
350
630
|
def get_domain_assignment(self):
|
|
631
|
+
"""
|
|
632
|
+
Retrieve the domain assignment of the TCR as a dict with variable domain type as key and chain ID as value.
|
|
633
|
+
|
|
634
|
+
Returns:
|
|
635
|
+
dict: domain assignment from domain to chain ID, e.g. {"VA": "D", "VB": "E"}
|
|
636
|
+
"""
|
|
351
637
|
try:
|
|
352
638
|
return {"VA": self.VA, "VB": self.VB}
|
|
353
639
|
except AttributeError:
|
|
@@ -358,6 +644,12 @@ class abTCR(TCR):
|
|
|
358
644
|
return None
|
|
359
645
|
|
|
360
646
|
def is_engineered(self):
|
|
647
|
+
"""
|
|
648
|
+
Flag for engineered TCRs.
|
|
649
|
+
|
|
650
|
+
Returns:
|
|
651
|
+
bool: Flag for engineered TCRs
|
|
652
|
+
"""
|
|
361
653
|
if self.engineered:
|
|
362
654
|
return True
|
|
363
655
|
else:
|
|
@@ -371,6 +663,12 @@ class abTCR(TCR):
|
|
|
371
663
|
return False
|
|
372
664
|
|
|
373
665
|
def get_fragments(self):
|
|
666
|
+
"""
|
|
667
|
+
Retrieve the fragments, ie FW and CDR loops of the TCR as a generator.
|
|
668
|
+
|
|
669
|
+
Yields:
|
|
670
|
+
Fragment: fragment of TCR chain.
|
|
671
|
+
"""
|
|
374
672
|
vb, va = self.get_VB(), self.get_VA()
|
|
375
673
|
|
|
376
674
|
# If a variable domain exists
|
|
@@ -379,6 +677,56 @@ class abTCR(TCR):
|
|
|
379
677
|
for frag in var_domain.get_fragments():
|
|
380
678
|
yield frag
|
|
381
679
|
|
|
680
|
+
def standardise_chain_names(self) -> None:
|
|
681
|
+
"""
|
|
682
|
+
Standardise the TCR, antigen, and MHC chain names to the following convention.
|
|
683
|
+
|
|
684
|
+
Convention:
|
|
685
|
+
- A - MHC chain 1
|
|
686
|
+
- B - MHC chain 2 (eg B2M)
|
|
687
|
+
- C - antigen chain
|
|
688
|
+
- D - TCR alpha chain
|
|
689
|
+
- E - TCR beta chain
|
|
690
|
+
|
|
691
|
+
Note, this mutates the original object.
|
|
692
|
+
|
|
693
|
+
Raises:
|
|
694
|
+
TCRError: if there is more than one antigen or MHC molecules attached to the TCR.
|
|
695
|
+
|
|
696
|
+
"""
|
|
697
|
+
self._validate_chain_standardising()
|
|
698
|
+
|
|
699
|
+
new_id = []
|
|
700
|
+
new_child_dict = {}
|
|
701
|
+
|
|
702
|
+
if hasattr(self, 'VB'):
|
|
703
|
+
new_child_dict['E'] = self.child_dict[self.VB]
|
|
704
|
+
self.VB = 'E'
|
|
705
|
+
new_id.append('E')
|
|
706
|
+
|
|
707
|
+
if hasattr(self, 'VA'):
|
|
708
|
+
new_child_dict['D'] = self.child_dict[self.VA]
|
|
709
|
+
self.VA = 'D'
|
|
710
|
+
new_id.append('D')
|
|
711
|
+
|
|
712
|
+
with warnings.catch_warnings():
|
|
713
|
+
warnings.simplefilter('ignore', BiopythonWarning)
|
|
714
|
+
|
|
715
|
+
for chain_id, chain in new_child_dict.items():
|
|
716
|
+
chain.id = chain_id
|
|
717
|
+
|
|
718
|
+
self.child_dict = new_child_dict
|
|
719
|
+
|
|
720
|
+
if hasattr(self, 'antigen') and self.antigen:
|
|
721
|
+
self._standardise_antigen_chain_names()
|
|
722
|
+
|
|
723
|
+
if hasattr(self, 'MHC') and self.MHC:
|
|
724
|
+
self._standardise_mhc_chain_names()
|
|
725
|
+
|
|
726
|
+
with warnings.catch_warnings():
|
|
727
|
+
warnings.simplefilter('ignore', BiopythonWarning)
|
|
728
|
+
self.id = ''.join(new_id)
|
|
729
|
+
|
|
382
730
|
|
|
383
731
|
class gdTCR(TCR):
|
|
384
732
|
|
|
@@ -455,6 +803,56 @@ class gdTCR(TCR):
|
|
|
455
803
|
for frag in var_domain.get_fragments():
|
|
456
804
|
yield frag
|
|
457
805
|
|
|
806
|
+
def standardise_chain_names(self) -> None:
|
|
807
|
+
"""
|
|
808
|
+
Standardise the TCR, antigen, and MHC chain names to the following convention.
|
|
809
|
+
|
|
810
|
+
Convention:
|
|
811
|
+
- A - MHC chain 1
|
|
812
|
+
- B - MHC chain 2 (eg B2M)
|
|
813
|
+
- C - antigen chain
|
|
814
|
+
- D - TCR delta chain
|
|
815
|
+
- E - TCR gamma chain
|
|
816
|
+
|
|
817
|
+
Note, this mutates the original object.
|
|
818
|
+
|
|
819
|
+
Raises:
|
|
820
|
+
TCRError: if there is more than one antigen or MHC molecules attached to the TCR.
|
|
821
|
+
|
|
822
|
+
"""
|
|
823
|
+
self._validate_chain_standardising()
|
|
824
|
+
|
|
825
|
+
new_id = []
|
|
826
|
+
new_child_dict = {}
|
|
827
|
+
|
|
828
|
+
if hasattr(self, 'VG'):
|
|
829
|
+
new_child_dict['E'] = self.child_dict[self.VG]
|
|
830
|
+
self.VG = 'E'
|
|
831
|
+
new_id.append('E')
|
|
832
|
+
|
|
833
|
+
if hasattr(self, 'VD'):
|
|
834
|
+
new_child_dict['D'] = self.child_dict[self.VD]
|
|
835
|
+
self.VD = 'D'
|
|
836
|
+
new_id.append('D')
|
|
837
|
+
|
|
838
|
+
with warnings.catch_warnings():
|
|
839
|
+
warnings.simplefilter('ignore', BiopythonWarning)
|
|
840
|
+
|
|
841
|
+
for chain_id, chain in new_child_dict.items():
|
|
842
|
+
chain.id = chain_id
|
|
843
|
+
|
|
844
|
+
self.child_dict = new_child_dict
|
|
845
|
+
|
|
846
|
+
if hasattr(self, 'antigen') and self.antigen:
|
|
847
|
+
self._standardise_antigen_chain_names()
|
|
848
|
+
|
|
849
|
+
if hasattr(self, 'MHC') and self.MHC:
|
|
850
|
+
self._standardise_mhc_chain_names()
|
|
851
|
+
|
|
852
|
+
with warnings.catch_warnings():
|
|
853
|
+
warnings.simplefilter('ignore', BiopythonWarning)
|
|
854
|
+
self.id = ''.join(new_id)
|
|
855
|
+
|
|
458
856
|
|
|
459
857
|
class dbTCR(TCR):
|
|
460
858
|
def __init__(self, c1, c2):
|
|
@@ -530,3 +928,53 @@ class dbTCR(TCR):
|
|
|
530
928
|
if var_domain:
|
|
531
929
|
for frag in var_domain.get_fragments():
|
|
532
930
|
yield frag
|
|
931
|
+
|
|
932
|
+
def standardise_chain_names(self) -> None:
|
|
933
|
+
"""
|
|
934
|
+
Standardise the TCR, antigen, and MHC chain names to the following convention.
|
|
935
|
+
|
|
936
|
+
Convention:
|
|
937
|
+
- A - MHC chain 1
|
|
938
|
+
- B - MHC chain 2 (eg B2M)
|
|
939
|
+
- C - antigen chain
|
|
940
|
+
- D - TCR delta chain
|
|
941
|
+
- E - TCR beta chain
|
|
942
|
+
|
|
943
|
+
Note, this mutates the original object.
|
|
944
|
+
|
|
945
|
+
Raises:
|
|
946
|
+
TCRError: if there is more than one antigen or MHC molecules attached to the TCR.
|
|
947
|
+
|
|
948
|
+
"""
|
|
949
|
+
self._validate_chain_standardising()
|
|
950
|
+
|
|
951
|
+
new_id = []
|
|
952
|
+
new_child_dict = {}
|
|
953
|
+
|
|
954
|
+
if hasattr(self, 'VB'):
|
|
955
|
+
new_child_dict['E'] = self.child_dict[self.VB]
|
|
956
|
+
self.VB = 'E'
|
|
957
|
+
new_id.append('E')
|
|
958
|
+
|
|
959
|
+
if hasattr(self, 'VD'):
|
|
960
|
+
new_child_dict['D'] = self.child_dict[self.VD]
|
|
961
|
+
self.VD = 'D'
|
|
962
|
+
new_id.append('D')
|
|
963
|
+
|
|
964
|
+
with warnings.catch_warnings():
|
|
965
|
+
warnings.simplefilter('ignore', BiopythonWarning)
|
|
966
|
+
|
|
967
|
+
for chain_id, chain in new_child_dict.items():
|
|
968
|
+
chain.id = chain_id
|
|
969
|
+
|
|
970
|
+
self.child_dict = new_child_dict
|
|
971
|
+
|
|
972
|
+
if hasattr(self, 'antigen') and self.antigen:
|
|
973
|
+
self._standardise_antigen_chain_names()
|
|
974
|
+
|
|
975
|
+
if hasattr(self, 'MHC') and self.MHC:
|
|
976
|
+
self._standardise_mhc_chain_names()
|
|
977
|
+
|
|
978
|
+
with warnings.catch_warnings():
|
|
979
|
+
warnings.simplefilter('ignore', BiopythonWarning)
|
|
980
|
+
self.id = ''.join(new_id)
|