stcrpy 1.0.0__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.
- examples/__init__.py +0 -0
- examples/egnn.py +425 -0
- stcrpy/__init__.py +5 -0
- stcrpy/tcr_datasets/__init__.py +0 -0
- stcrpy/tcr_datasets/tcr_graph_dataset.py +499 -0
- stcrpy/tcr_datasets/tcr_selector.py +0 -0
- stcrpy/tcr_datasets/tcr_structure_dataset.py +0 -0
- stcrpy/tcr_datasets/utils.py +350 -0
- stcrpy/tcr_formats/__init__.py +0 -0
- stcrpy/tcr_formats/tcr_formats.py +114 -0
- stcrpy/tcr_formats/tcr_haddock.py +556 -0
- stcrpy/tcr_geometry/TCRCoM.py +350 -0
- stcrpy/tcr_geometry/TCRCoM_LICENCE +168 -0
- stcrpy/tcr_geometry/TCRDock.py +261 -0
- stcrpy/tcr_geometry/TCRGeom.py +450 -0
- stcrpy/tcr_geometry/TCRGeomFiltering.py +273 -0
- stcrpy/tcr_geometry/__init__.py +0 -0
- stcrpy/tcr_geometry/reference_data/__init__.py +0 -0
- stcrpy/tcr_geometry/reference_data/dock_reference_1_imgt_numbered.pdb +6549 -0
- stcrpy/tcr_geometry/reference_data/dock_reference_2_imgt_numbered.pdb +6495 -0
- stcrpy/tcr_geometry/reference_data/reference_A.pdb +31 -0
- stcrpy/tcr_geometry/reference_data/reference_B.pdb +31 -0
- stcrpy/tcr_geometry/reference_data/reference_D.pdb +31 -0
- stcrpy/tcr_geometry/reference_data/reference_G.pdb +31 -0
- stcrpy/tcr_geometry/reference_data/reference_data.py +104 -0
- stcrpy/tcr_interactions/PLIPParser.py +147 -0
- stcrpy/tcr_interactions/TCRInteractionProfiler.py +433 -0
- stcrpy/tcr_interactions/TCRpMHC_PLIP_Model_Parser.py +133 -0
- stcrpy/tcr_interactions/__init__.py +0 -0
- stcrpy/tcr_interactions/utils.py +170 -0
- stcrpy/tcr_methods/__init__.py +0 -0
- stcrpy/tcr_methods/tcr_batch_operations.py +223 -0
- stcrpy/tcr_methods/tcr_methods.py +150 -0
- stcrpy/tcr_methods/tcr_reformatting.py +18 -0
- stcrpy/tcr_metrics/__init__.py +2 -0
- stcrpy/tcr_metrics/constants.py +39 -0
- stcrpy/tcr_metrics/tcr_interface_rmsd.py +237 -0
- stcrpy/tcr_metrics/tcr_rmsd.py +179 -0
- stcrpy/tcr_ml/__init__.py +0 -0
- stcrpy/tcr_ml/geometry_predictor.py +3 -0
- stcrpy/tcr_processing/AGchain.py +89 -0
- stcrpy/tcr_processing/Chemical_components.py +48915 -0
- stcrpy/tcr_processing/Entity.py +301 -0
- stcrpy/tcr_processing/Fragment.py +58 -0
- stcrpy/tcr_processing/Holder.py +24 -0
- stcrpy/tcr_processing/MHC.py +449 -0
- stcrpy/tcr_processing/MHCchain.py +149 -0
- stcrpy/tcr_processing/Model.py +37 -0
- stcrpy/tcr_processing/Select.py +145 -0
- stcrpy/tcr_processing/TCR.py +532 -0
- stcrpy/tcr_processing/TCRIO.py +47 -0
- stcrpy/tcr_processing/TCRParser.py +1230 -0
- stcrpy/tcr_processing/TCRStructure.py +148 -0
- stcrpy/tcr_processing/TCRchain.py +160 -0
- stcrpy/tcr_processing/__init__.py +3 -0
- stcrpy/tcr_processing/annotate.py +480 -0
- stcrpy/tcr_processing/utils/__init__.py +0 -0
- stcrpy/tcr_processing/utils/common.py +67 -0
- stcrpy/tcr_processing/utils/constants.py +367 -0
- stcrpy/tcr_processing/utils/region_definitions.py +782 -0
- stcrpy/utils/__init__.py +0 -0
- stcrpy/utils/error_stream.py +12 -0
- stcrpy-1.0.0.dist-info/METADATA +173 -0
- stcrpy-1.0.0.dist-info/RECORD +68 -0
- stcrpy-1.0.0.dist-info/WHEEL +5 -0
- stcrpy-1.0.0.dist-info/licenses/LICENCE +28 -0
- stcrpy-1.0.0.dist-info/licenses/stcrpy/tcr_geometry/TCRCoM_LICENCE +168 -0
- stcrpy-1.0.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Select.py
|
|
3
|
+
Created on 9 May 2017
|
|
4
|
+
@author: leem
|
|
5
|
+
|
|
6
|
+
These are selection classes for the save method of the TcrPDB entity
|
|
7
|
+
They are based on the ABDB.AbPDB.Select and Bio.PDB.PDBIO Selection classes
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from .utils.constants import BACKBONE_CB
|
|
12
|
+
from .utils.common import *
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class select_all(object):
|
|
16
|
+
"""
|
|
17
|
+
Default selection (everything) during writing - can be used as base class
|
|
18
|
+
to implement selective output. This selects which entities will be written out.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __repr__(self):
|
|
22
|
+
return "<Select all>"
|
|
23
|
+
|
|
24
|
+
def accept(self, ob):
|
|
25
|
+
if ob.level == "A":
|
|
26
|
+
return self.accept_atom(ob)
|
|
27
|
+
elif ob.level == "R":
|
|
28
|
+
return self.accept_residue(ob)
|
|
29
|
+
elif ob.level == "C":
|
|
30
|
+
return self.accept_chain(ob)
|
|
31
|
+
elif ob.level == "F":
|
|
32
|
+
return self.accept_fragment(ob)
|
|
33
|
+
elif ob.level == "H":
|
|
34
|
+
return self.accept_holder(ob)
|
|
35
|
+
elif ob.level == "M":
|
|
36
|
+
return self.accept_model(ob)
|
|
37
|
+
|
|
38
|
+
def accept_model(self, model):
|
|
39
|
+
"""
|
|
40
|
+
Overload this to reject models for output.
|
|
41
|
+
"""
|
|
42
|
+
return 1
|
|
43
|
+
|
|
44
|
+
def accept_holder(self, model):
|
|
45
|
+
"""
|
|
46
|
+
Overload this to reject holders for output. (TCRs, TCRchains-holder, MHCchains-holder, AGchains-holder)
|
|
47
|
+
"""
|
|
48
|
+
return 1
|
|
49
|
+
|
|
50
|
+
def accept_chain(self, chain):
|
|
51
|
+
"""
|
|
52
|
+
Overload this to reject chains for output.
|
|
53
|
+
"""
|
|
54
|
+
return 1
|
|
55
|
+
|
|
56
|
+
def accept_fragment(self, fragment):
|
|
57
|
+
"""
|
|
58
|
+
Overload this to reject residues for output.
|
|
59
|
+
"""
|
|
60
|
+
return 1
|
|
61
|
+
|
|
62
|
+
def accept_residue(self, residue):
|
|
63
|
+
"""
|
|
64
|
+
Overload this to reject residues for output.
|
|
65
|
+
"""
|
|
66
|
+
return 1
|
|
67
|
+
|
|
68
|
+
def accept_atom(self, atom):
|
|
69
|
+
"""
|
|
70
|
+
Overload this to reject atoms for output.
|
|
71
|
+
"""
|
|
72
|
+
return 1
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class variable_only(select_all):
|
|
76
|
+
"""
|
|
77
|
+
Select the variable region(s) of the structure.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def __repr__(self):
|
|
81
|
+
return "<variable_only>"
|
|
82
|
+
|
|
83
|
+
def accept_holder(self, holder):
|
|
84
|
+
"""
|
|
85
|
+
Overload this to reject holders for output.
|
|
86
|
+
"""
|
|
87
|
+
# Accept an abTCR or a gdTCR
|
|
88
|
+
if (
|
|
89
|
+
(hasattr(holder, "VB") and hasattr(holder, "VA"))
|
|
90
|
+
or (hasattr(holder, "VD") and hasattr(holder, "VG"))
|
|
91
|
+
or (hasattr(holder, "VB") and hasattr(holder, "VD"))
|
|
92
|
+
):
|
|
93
|
+
return 1
|
|
94
|
+
else:
|
|
95
|
+
return 0
|
|
96
|
+
|
|
97
|
+
def accept_residue(self, residue):
|
|
98
|
+
"""
|
|
99
|
+
Overload this to reject residues for output.
|
|
100
|
+
"""
|
|
101
|
+
if hasattr(residue, "region") and residue.region != "?":
|
|
102
|
+
return 1
|
|
103
|
+
else:
|
|
104
|
+
return 0
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class cdr3(variable_only):
|
|
108
|
+
"""
|
|
109
|
+
Select only CDR3.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
def __repr__(self):
|
|
113
|
+
return "<CDR3>"
|
|
114
|
+
|
|
115
|
+
def accept_residue(self, residue):
|
|
116
|
+
if "cdr" in residue.region and "3" in residue.region:
|
|
117
|
+
return 1
|
|
118
|
+
else:
|
|
119
|
+
return 0
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class backbone(select_all):
|
|
123
|
+
"""
|
|
124
|
+
Select only backbone (no side chains) atoms in the structure.
|
|
125
|
+
Backbone defined as "C","CA","N","CB" and "O" atom identifiers in amino acid (pdb notation)
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
def __repr__(self):
|
|
129
|
+
return "<backbone>"
|
|
130
|
+
|
|
131
|
+
def accept_atom(self, atom):
|
|
132
|
+
if atom.id in BACKBONE_CB:
|
|
133
|
+
return 1
|
|
134
|
+
else:
|
|
135
|
+
return 0
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class fv_only_backbone(variable_only, backbone):
|
|
139
|
+
"""
|
|
140
|
+
Select the backbone atoms of the variable region.
|
|
141
|
+
Example of combining selection classes.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
def __repr__(self):
|
|
145
|
+
return "<variable only backbone>"
|
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Created on 3rd April 2024
|
|
3
|
+
@author: Nele Quast based on work by Dunbar and Leem
|
|
4
|
+
The TCR class.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
import warnings
|
|
9
|
+
|
|
10
|
+
from .Entity import Entity
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from .. import tcr_interactions
|
|
14
|
+
except ImportError as e:
|
|
15
|
+
warnings.warn(
|
|
16
|
+
"TCR interaction profiling could not be imported. Check PLIP installation"
|
|
17
|
+
)
|
|
18
|
+
print(e)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TCR(Entity):
|
|
22
|
+
"""
|
|
23
|
+
TCR class. This is generic which is inherited later.
|
|
24
|
+
Holds paired TCR chains.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def _add_antigen(self, antigen=None):
|
|
28
|
+
if antigen not in self.antigen:
|
|
29
|
+
self.antigen.append(antigen)
|
|
30
|
+
|
|
31
|
+
def _add_mhc(self, mhc=None):
|
|
32
|
+
if mhc not in self.MHC:
|
|
33
|
+
self.MHC.append(mhc)
|
|
34
|
+
# If there are any het antigens that are in the MHC but not in close proximity of the TCR
|
|
35
|
+
# (e.g. 4x6c antigen) then add it to the TCR.
|
|
36
|
+
if set(mhc.antigen) - set(self.antigen):
|
|
37
|
+
self.antigen.extend(mhc.antigen)
|
|
38
|
+
|
|
39
|
+
def get_antigen(self):
|
|
40
|
+
"""
|
|
41
|
+
Return a list of bound antigens.
|
|
42
|
+
"""
|
|
43
|
+
return self.antigen
|
|
44
|
+
|
|
45
|
+
def get_MHC(self):
|
|
46
|
+
""" """
|
|
47
|
+
return self.MHC
|
|
48
|
+
|
|
49
|
+
def is_bound(self):
|
|
50
|
+
"""
|
|
51
|
+
Check whether this TCR is bound to an antigen
|
|
52
|
+
"""
|
|
53
|
+
if self.get_antigen():
|
|
54
|
+
return True
|
|
55
|
+
else:
|
|
56
|
+
return False
|
|
57
|
+
|
|
58
|
+
def get_chains(self):
|
|
59
|
+
for c in self:
|
|
60
|
+
yield c
|
|
61
|
+
|
|
62
|
+
def get_residues(self):
|
|
63
|
+
for c in self.get_chains():
|
|
64
|
+
for r in c:
|
|
65
|
+
yield r
|
|
66
|
+
|
|
67
|
+
def get_atoms(self):
|
|
68
|
+
for r in self.get_residues():
|
|
69
|
+
for a in r:
|
|
70
|
+
yield a
|
|
71
|
+
|
|
72
|
+
def get_frameworks(self):
|
|
73
|
+
"""
|
|
74
|
+
Obtain framework regions from a TCR structure object.
|
|
75
|
+
"""
|
|
76
|
+
for f in self.get_fragments():
|
|
77
|
+
if "fw" in f.id:
|
|
78
|
+
yield f
|
|
79
|
+
|
|
80
|
+
def get_CDRs(self):
|
|
81
|
+
"""
|
|
82
|
+
Obtain CDR loops from a TCR structure object
|
|
83
|
+
"""
|
|
84
|
+
for f in self.get_fragments():
|
|
85
|
+
if "cdr" in f.id:
|
|
86
|
+
yield f
|
|
87
|
+
|
|
88
|
+
def get_TCR_type(self):
|
|
89
|
+
"""
|
|
90
|
+
Get the TCR type
|
|
91
|
+
"""
|
|
92
|
+
if hasattr(self, "tcr_type"):
|
|
93
|
+
return self.tcr_type
|
|
94
|
+
elif hasattr(self, "VB") and hasattr(self, "VA"):
|
|
95
|
+
self.tcr_type = "abTCR"
|
|
96
|
+
return self.tcr_type
|
|
97
|
+
elif hasattr(self, "VD") and hasattr(self, "VG"):
|
|
98
|
+
self.tcr_type = "gdTCR"
|
|
99
|
+
return self.tcr_type
|
|
100
|
+
elif hasattr(self, "VB") and hasattr(self, "VD"):
|
|
101
|
+
self.tcr_type = "dbTCR"
|
|
102
|
+
return self.tcr_type
|
|
103
|
+
|
|
104
|
+
def get_germline_assignments(self):
|
|
105
|
+
return {c.id: c.get_germline_assignments() for c in self.get_chains()}
|
|
106
|
+
|
|
107
|
+
def get_MHC_allele_assignments(self):
|
|
108
|
+
return [
|
|
109
|
+
(
|
|
110
|
+
mhc.get_allele_assignments()
|
|
111
|
+
if mhc.level
|
|
112
|
+
!= "C" # results in identical nesting structure for MHC and MHCchain types
|
|
113
|
+
else {mhc.id: mhc.get_allele_assignments()}
|
|
114
|
+
)
|
|
115
|
+
for mhc in self.get_MHC()
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
def get_germlines_and_alleles(self):
|
|
119
|
+
from ..tcr_formats.tcr_formats import get_sequences
|
|
120
|
+
|
|
121
|
+
germlines_and_alleles = {}
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
germlines = self.get_germline_assignments()
|
|
125
|
+
for tcr_domain, c in self.get_domain_assignment().items():
|
|
126
|
+
germlines_and_alleles[tcr_domain] = (
|
|
127
|
+
germlines[c]["v_gene"][0][1],
|
|
128
|
+
germlines[c]["j_gene"][0][1],
|
|
129
|
+
)
|
|
130
|
+
germlines_and_alleles[f"{tcr_domain}_species"] = sorted(
|
|
131
|
+
tuple(
|
|
132
|
+
set(
|
|
133
|
+
(
|
|
134
|
+
germlines[c]["v_gene"][0][0],
|
|
135
|
+
germlines[c]["j_gene"][0][0],
|
|
136
|
+
)
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
germlines_and_alleles[f"TCR_{tcr_domain}_seq"] = get_sequences(self[c])[
|
|
141
|
+
c
|
|
142
|
+
]
|
|
143
|
+
if len(self.get_MHC()) == 1:
|
|
144
|
+
MHC = self.get_MHC()[0]
|
|
145
|
+
alleles = self.get_MHC_allele_assignments()[0]
|
|
146
|
+
germlines_and_alleles["MHC_type"] = (
|
|
147
|
+
MHC.get_MHC_type() if MHC.level != "C" else MHC.chain_type
|
|
148
|
+
)
|
|
149
|
+
MHC_domains = {list(d.keys())[0]: c for c, d in alleles.items()}
|
|
150
|
+
for d, c in MHC_domains.items():
|
|
151
|
+
germlines_and_alleles[f"MHC_{d}"] = alleles[c][d][0][1]
|
|
152
|
+
germlines_and_alleles[f"MHC_{d}_seq"] = (
|
|
153
|
+
get_sequences(MHC[c])[c]
|
|
154
|
+
if MHC.level != "C"
|
|
155
|
+
else get_sequences(MHC)[c]
|
|
156
|
+
)
|
|
157
|
+
germlines_and_alleles["antigen"] = (
|
|
158
|
+
get_sequences(self.get_antigen()[0])[self.get_antigen()[0].id]
|
|
159
|
+
if len(self.get_antigen()) == 1
|
|
160
|
+
else None
|
|
161
|
+
)
|
|
162
|
+
except Exception as e:
|
|
163
|
+
warnings.warn(
|
|
164
|
+
f"Germline and allele retrieval failed for {self} with error {str(e)}"
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
return germlines_and_alleles
|
|
168
|
+
|
|
169
|
+
def save(self, save_as=None, tcr_only: bool = False, format: str = "pdb"):
|
|
170
|
+
from . import TCRIO
|
|
171
|
+
|
|
172
|
+
tcrio = TCRIO.TCRIO()
|
|
173
|
+
tcrio.save(self, save_as=save_as, tcr_only=tcr_only, format=format)
|
|
174
|
+
|
|
175
|
+
def get_scanning_angle(self, mode="rudolph"):
|
|
176
|
+
if not hasattr(self, "geometry") or self.geometry.mode != mode:
|
|
177
|
+
self.calculate_docking_geometry(mode=mode)
|
|
178
|
+
return self.geometry.get_scanning_angle()
|
|
179
|
+
|
|
180
|
+
def get_pitch_angle(self, mode="rudolph"):
|
|
181
|
+
if not hasattr(self, "geometry") or self.geometry.mode != mode:
|
|
182
|
+
self.calculate_docking_geometry(mode=mode)
|
|
183
|
+
return self.geometry.get_pitch_angle()
|
|
184
|
+
|
|
185
|
+
def calculate_docking_geometry(self, mode="rudolph", as_df=False):
|
|
186
|
+
if len(self.get_MHC()) == 0:
|
|
187
|
+
warnings.warn(
|
|
188
|
+
f"No MHC found for TCR {self}. Docking geometry cannot be calcuated"
|
|
189
|
+
)
|
|
190
|
+
return None
|
|
191
|
+
|
|
192
|
+
try: # import here to avoid circular imports
|
|
193
|
+
from ..tcr_geometry.TCRGeom import TCRGeom
|
|
194
|
+
except ImportError as e:
|
|
195
|
+
warnings.warn(
|
|
196
|
+
"TCR geometry calculation could not be imported. Check installation"
|
|
197
|
+
)
|
|
198
|
+
raise ImportError(str(e))
|
|
199
|
+
|
|
200
|
+
self.geometry = TCRGeom(self, mode=mode)
|
|
201
|
+
if as_df:
|
|
202
|
+
return self.geometry.to_df()
|
|
203
|
+
return self.geometry.to_dict()
|
|
204
|
+
|
|
205
|
+
def score_docking_geometry(self, **kwargs):
|
|
206
|
+
from ..tcr_geometry.TCRGeomFiltering import DockingGeometryFilter
|
|
207
|
+
|
|
208
|
+
geom_filter = DockingGeometryFilter()
|
|
209
|
+
if not hasattr(self, "geometry"):
|
|
210
|
+
self.calculate_docking_geometry(mode="com")
|
|
211
|
+
return geom_filter.score_docking_geometry(
|
|
212
|
+
self.geometry.get_scanning_angle(),
|
|
213
|
+
self.geometry.get_pitch_angle(),
|
|
214
|
+
self.geometry.tcr_com[-1], # z component of TCR centre of mass
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
def profile_peptide_interactions(
|
|
218
|
+
self, renumber: bool = True, save_to: str = None, **kwargs
|
|
219
|
+
) -> "pd.DataFrame":
|
|
220
|
+
if len(self.get_antigen()) == 0:
|
|
221
|
+
warnings.warn(
|
|
222
|
+
f"No peptide antigen found for TCR {self}. Peptide interactions cannot be profiled"
|
|
223
|
+
)
|
|
224
|
+
return None
|
|
225
|
+
|
|
226
|
+
if "PLIPParser" not in [m.split(".")[-1] for m in sys.modules]:
|
|
227
|
+
warnings.warn(
|
|
228
|
+
"TCR interactions module was not imported. Check warning log and PLIP installation"
|
|
229
|
+
)
|
|
230
|
+
return None
|
|
231
|
+
|
|
232
|
+
from ..tcr_interactions import TCRInteractionProfiler
|
|
233
|
+
|
|
234
|
+
interaction_profiler = TCRInteractionProfiler.TCRInteractionProfiler(**kwargs)
|
|
235
|
+
interactions = interaction_profiler.get_interactions(
|
|
236
|
+
self, renumber=renumber, save_as_csv=save_to
|
|
237
|
+
)
|
|
238
|
+
return interactions
|
|
239
|
+
|
|
240
|
+
def get_interaction_heatmap(self, plotting_kwargs={}, **interaction_kwargs):
|
|
241
|
+
from ..tcr_interactions import TCRInteractionProfiler
|
|
242
|
+
|
|
243
|
+
interaction_profiler = TCRInteractionProfiler.TCRInteractionProfiler(
|
|
244
|
+
**interaction_kwargs
|
|
245
|
+
)
|
|
246
|
+
interaction_profiler.get_interaction_heatmap(self, **plotting_kwargs)
|
|
247
|
+
|
|
248
|
+
def profile_TCR_interactions(self):
|
|
249
|
+
raise NotImplementedError
|
|
250
|
+
|
|
251
|
+
def profile_MHC_interactions(self):
|
|
252
|
+
raise NotImplementedError
|
|
253
|
+
|
|
254
|
+
def _create_interaction_visualiser(self):
|
|
255
|
+
"""Function called during TCR initialisation checks if pymol is installed and assigns a visualisation method accordingly.
|
|
256
|
+
If pymol is installed, method to generate interaction visualisations is returned.
|
|
257
|
+
If pymol is not installed, calling the visualisation
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
callable: TCR bound method to visualise interactions of the TCR and MHC to the peptide.
|
|
262
|
+
"""
|
|
263
|
+
try:
|
|
264
|
+
import pymol
|
|
265
|
+
|
|
266
|
+
def visualise_interactions(
|
|
267
|
+
save_as=None, antigen_residues_to_highlight=None, **interaction_kwargs
|
|
268
|
+
):
|
|
269
|
+
from ..tcr_interactions import TCRInteractionProfiler
|
|
270
|
+
|
|
271
|
+
interaction_profiler = TCRInteractionProfiler.TCRInteractionProfiler(
|
|
272
|
+
**interaction_kwargs
|
|
273
|
+
)
|
|
274
|
+
interaction_session_file = interaction_profiler.create_pymol_session(
|
|
275
|
+
self,
|
|
276
|
+
save_as=save_as,
|
|
277
|
+
antigen_residues_to_highlight=antigen_residues_to_highlight,
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
return interaction_session_file
|
|
281
|
+
|
|
282
|
+
return visualise_interactions
|
|
283
|
+
|
|
284
|
+
except ModuleNotFoundError:
|
|
285
|
+
|
|
286
|
+
def visualise_interactions(**interaction_kwargs):
|
|
287
|
+
warnings.warn(
|
|
288
|
+
f"""pymol was not imported. Interactions were not visualised.
|
|
289
|
+
\nTo enable pymol visualisations please install pymol in a conda environment with:
|
|
290
|
+
\nconda install -c conda-forge -c schrodinger numpy pymol-bundle\n\n
|
|
291
|
+
"""
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
return visualise_interactions
|
|
295
|
+
|
|
296
|
+
except ImportError as e:
|
|
297
|
+
|
|
298
|
+
def visualise_interactions(import_error=e, **interaction_kwargs):
|
|
299
|
+
warnings.warn(
|
|
300
|
+
f"""pymol was not imported. Interactions were not visualised. This is due to an import error. Perhaps try reinstalling pymol?
|
|
301
|
+
\nThe error trace was: {str(import_error)}
|
|
302
|
+
"""
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
return visualise_interactions
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class abTCR(TCR):
|
|
309
|
+
def __init__(self, c1, c2):
|
|
310
|
+
|
|
311
|
+
if c1.chain_type == "B":
|
|
312
|
+
Entity.__init__(self, c1.id + c2.id)
|
|
313
|
+
else:
|
|
314
|
+
Entity.__init__(self, c2.id + c1.id)
|
|
315
|
+
|
|
316
|
+
# The TCR is a Holder class
|
|
317
|
+
self.level = "H"
|
|
318
|
+
self._add_domain(c1)
|
|
319
|
+
self._add_domain(c2)
|
|
320
|
+
self.child_list = sorted(
|
|
321
|
+
self.child_list, key=lambda x: x.chain_type, reverse=True
|
|
322
|
+
) # make sure that the list goes B->A or G->D
|
|
323
|
+
self.antigen = []
|
|
324
|
+
self.MHC = []
|
|
325
|
+
self.engineered = False
|
|
326
|
+
self.scTCR = False # This is rare but does happen
|
|
327
|
+
|
|
328
|
+
self.visualise_interactions = self._create_interaction_visualiser()
|
|
329
|
+
|
|
330
|
+
def __repr__(self):
|
|
331
|
+
return "<TCR %s%s beta=%s; alpha=%s>" % (self.VB, self.VA, self.VB, self.VA)
|
|
332
|
+
|
|
333
|
+
def _add_domain(self, chain):
|
|
334
|
+
if chain.chain_type == "B":
|
|
335
|
+
self.VB = chain.id
|
|
336
|
+
elif chain.chain_type == "A" or chain.chain_type == "D":
|
|
337
|
+
self.VA = chain.id
|
|
338
|
+
|
|
339
|
+
# Add the chain as a child of this entity.
|
|
340
|
+
self.add(chain)
|
|
341
|
+
|
|
342
|
+
def get_VB(self):
|
|
343
|
+
if hasattr(self, "VB"):
|
|
344
|
+
return self.child_dict[self.VB]
|
|
345
|
+
|
|
346
|
+
def get_VA(self):
|
|
347
|
+
if hasattr(self, "VA"):
|
|
348
|
+
return self.child_dict[self.VA]
|
|
349
|
+
|
|
350
|
+
def get_domain_assignment(self):
|
|
351
|
+
try:
|
|
352
|
+
return {"VA": self.VA, "VB": self.VB}
|
|
353
|
+
except AttributeError:
|
|
354
|
+
if hasattr(self, "VB"):
|
|
355
|
+
return {"VB": self.VB}
|
|
356
|
+
if hasattr(self, "VA"):
|
|
357
|
+
return {"VA": self.VA}
|
|
358
|
+
return None
|
|
359
|
+
|
|
360
|
+
def is_engineered(self):
|
|
361
|
+
if self.engineered:
|
|
362
|
+
return True
|
|
363
|
+
else:
|
|
364
|
+
vb, va = self.get_VB(), self.get_VA()
|
|
365
|
+
for var_domain in [vb, va]:
|
|
366
|
+
if var_domain and var_domain.is_engineered():
|
|
367
|
+
self.engineered = True
|
|
368
|
+
return self.engineered
|
|
369
|
+
|
|
370
|
+
self.engineered = False
|
|
371
|
+
return False
|
|
372
|
+
|
|
373
|
+
def get_fragments(self):
|
|
374
|
+
vb, va = self.get_VB(), self.get_VA()
|
|
375
|
+
|
|
376
|
+
# If a variable domain exists
|
|
377
|
+
for var_domain in [vb, va]:
|
|
378
|
+
if var_domain:
|
|
379
|
+
for frag in var_domain.get_fragments():
|
|
380
|
+
yield frag
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
class gdTCR(TCR):
|
|
384
|
+
|
|
385
|
+
def __init__(self, c1, c2):
|
|
386
|
+
|
|
387
|
+
if c1.chain_type == "D":
|
|
388
|
+
Entity.__init__(self, c1.id + c2.id)
|
|
389
|
+
else:
|
|
390
|
+
Entity.__init__(self, c2.id + c1.id)
|
|
391
|
+
|
|
392
|
+
# The TCR is a Holder class
|
|
393
|
+
self.level = "H"
|
|
394
|
+
self._add_domain(c1)
|
|
395
|
+
self._add_domain(c2)
|
|
396
|
+
self.child_list = sorted(
|
|
397
|
+
self.child_list, key=lambda x: x.chain_type
|
|
398
|
+
) # make sure that the list goes B->A or D->G
|
|
399
|
+
self.antigen = []
|
|
400
|
+
self.MHC = []
|
|
401
|
+
self.engineered = False
|
|
402
|
+
self.scTCR = False # This is rare but does happen
|
|
403
|
+
|
|
404
|
+
self.visualise_interactions = self._create_interaction_visualiser()
|
|
405
|
+
|
|
406
|
+
def __repr__(self):
|
|
407
|
+
return "<TCR %s%s delta=%s; gamma=%s>" % (self.VD, self.VG, self.VD, self.VG)
|
|
408
|
+
|
|
409
|
+
def _add_domain(self, chain):
|
|
410
|
+
if chain.chain_type == "D":
|
|
411
|
+
self.VD = chain.id
|
|
412
|
+
elif chain.chain_type == "G":
|
|
413
|
+
self.VG = chain.id
|
|
414
|
+
|
|
415
|
+
# Add the chain as a child of this entity.
|
|
416
|
+
self.add(chain)
|
|
417
|
+
|
|
418
|
+
def get_VD(self):
|
|
419
|
+
if hasattr(self, "VD"):
|
|
420
|
+
return self.child_dict[self.VD]
|
|
421
|
+
|
|
422
|
+
def get_VG(self):
|
|
423
|
+
if hasattr(self, "VG"):
|
|
424
|
+
return self.child_dict[self.VG]
|
|
425
|
+
|
|
426
|
+
def get_domain_assignment(self):
|
|
427
|
+
try:
|
|
428
|
+
return {"VG": self.VG, "VD": self.VD}
|
|
429
|
+
except AttributeError:
|
|
430
|
+
if hasattr(self, "VD"):
|
|
431
|
+
return {"VD": self.VD}
|
|
432
|
+
if hasattr(self, "VG"):
|
|
433
|
+
return {"VG": self.VG}
|
|
434
|
+
return None
|
|
435
|
+
|
|
436
|
+
def is_engineered(self):
|
|
437
|
+
if self.engineered:
|
|
438
|
+
return True
|
|
439
|
+
else:
|
|
440
|
+
vd, vg = self.get_VD(), self.get_VG()
|
|
441
|
+
for var_domain in [vd, vg]:
|
|
442
|
+
if var_domain and var_domain.is_engineered():
|
|
443
|
+
self.engineered = True
|
|
444
|
+
return self.engineered
|
|
445
|
+
|
|
446
|
+
self.engineered = False
|
|
447
|
+
return False
|
|
448
|
+
|
|
449
|
+
def get_fragments(self):
|
|
450
|
+
vd, vg = self.get_VD(), self.get_VG()
|
|
451
|
+
|
|
452
|
+
# If a variable domain exists
|
|
453
|
+
for var_domain in [vg, vd]:
|
|
454
|
+
if var_domain:
|
|
455
|
+
for frag in var_domain.get_fragments():
|
|
456
|
+
yield frag
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
class dbTCR(TCR):
|
|
460
|
+
def __init__(self, c1, c2):
|
|
461
|
+
super(TCR, self).__init__()
|
|
462
|
+
|
|
463
|
+
if c1.chain_type == "B":
|
|
464
|
+
Entity.__init__(self, c1.id + c2.id)
|
|
465
|
+
else:
|
|
466
|
+
Entity.__init__(self, c2.id + c1.id)
|
|
467
|
+
|
|
468
|
+
# The TCR is a Holder class
|
|
469
|
+
self.level = "H"
|
|
470
|
+
self._add_domain(c1)
|
|
471
|
+
self._add_domain(c2)
|
|
472
|
+
self.child_list = sorted(
|
|
473
|
+
self.child_list, key=lambda x: x.chain_type, reverse=False
|
|
474
|
+
) # make sure that the list goes B->D
|
|
475
|
+
self.antigen = []
|
|
476
|
+
self.MHC = []
|
|
477
|
+
self.engineered = False
|
|
478
|
+
self.scTCR = False # This is rare but does happen
|
|
479
|
+
|
|
480
|
+
self.visualise_interactions = self._create_interaction_visualiser()
|
|
481
|
+
|
|
482
|
+
def __repr__(self):
|
|
483
|
+
return "<TCR %s%s beta=%s; delta=%s>" % (self.VB, self.VD, self.VB, self.VD)
|
|
484
|
+
|
|
485
|
+
def _add_domain(self, chain):
|
|
486
|
+
if chain.chain_type == "B":
|
|
487
|
+
self.VB = chain.id
|
|
488
|
+
elif chain.chain_type == "D":
|
|
489
|
+
self.VD = chain.id
|
|
490
|
+
|
|
491
|
+
# Add the chain as a child of this entity.
|
|
492
|
+
self.add(chain)
|
|
493
|
+
|
|
494
|
+
def get_VB(self):
|
|
495
|
+
if hasattr(self, "VB"):
|
|
496
|
+
return self.child_dict[self.VB]
|
|
497
|
+
|
|
498
|
+
def get_VD(self):
|
|
499
|
+
if hasattr(self, "VD"):
|
|
500
|
+
return self.child_dict[self.VD]
|
|
501
|
+
|
|
502
|
+
def get_domain_assignment(self):
|
|
503
|
+
try:
|
|
504
|
+
return {"VD": self.VD, "VB": self.VB}
|
|
505
|
+
except AttributeError:
|
|
506
|
+
if hasattr(self, "VB"):
|
|
507
|
+
return {"VB": self.VB}
|
|
508
|
+
if hasattr(self, "VD"):
|
|
509
|
+
return {"VD": self.VD}
|
|
510
|
+
return None
|
|
511
|
+
|
|
512
|
+
def is_engineered(self):
|
|
513
|
+
if self.engineered:
|
|
514
|
+
return True
|
|
515
|
+
else:
|
|
516
|
+
vb, vd = self.get_VB(), self.get_VD()
|
|
517
|
+
for var_domain in [vb, vd]:
|
|
518
|
+
if var_domain and var_domain.is_engineered():
|
|
519
|
+
self.engineered = True
|
|
520
|
+
return self.engineered
|
|
521
|
+
|
|
522
|
+
self.engineered = False
|
|
523
|
+
return False
|
|
524
|
+
|
|
525
|
+
def get_fragments(self):
|
|
526
|
+
vb, vd = self.get_VB(), self.get_VD()
|
|
527
|
+
|
|
528
|
+
# If a variable domain exists
|
|
529
|
+
for var_domain in [vb, vd]:
|
|
530
|
+
if var_domain:
|
|
531
|
+
for frag in var_domain.get_fragments():
|
|
532
|
+
yield frag
|