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.
Files changed (68) hide show
  1. examples/__init__.py +0 -0
  2. examples/egnn.py +425 -0
  3. stcrpy/__init__.py +5 -0
  4. stcrpy/tcr_datasets/__init__.py +0 -0
  5. stcrpy/tcr_datasets/tcr_graph_dataset.py +499 -0
  6. stcrpy/tcr_datasets/tcr_selector.py +0 -0
  7. stcrpy/tcr_datasets/tcr_structure_dataset.py +0 -0
  8. stcrpy/tcr_datasets/utils.py +350 -0
  9. stcrpy/tcr_formats/__init__.py +0 -0
  10. stcrpy/tcr_formats/tcr_formats.py +114 -0
  11. stcrpy/tcr_formats/tcr_haddock.py +556 -0
  12. stcrpy/tcr_geometry/TCRCoM.py +350 -0
  13. stcrpy/tcr_geometry/TCRCoM_LICENCE +168 -0
  14. stcrpy/tcr_geometry/TCRDock.py +261 -0
  15. stcrpy/tcr_geometry/TCRGeom.py +450 -0
  16. stcrpy/tcr_geometry/TCRGeomFiltering.py +273 -0
  17. stcrpy/tcr_geometry/__init__.py +0 -0
  18. stcrpy/tcr_geometry/reference_data/__init__.py +0 -0
  19. stcrpy/tcr_geometry/reference_data/dock_reference_1_imgt_numbered.pdb +6549 -0
  20. stcrpy/tcr_geometry/reference_data/dock_reference_2_imgt_numbered.pdb +6495 -0
  21. stcrpy/tcr_geometry/reference_data/reference_A.pdb +31 -0
  22. stcrpy/tcr_geometry/reference_data/reference_B.pdb +31 -0
  23. stcrpy/tcr_geometry/reference_data/reference_D.pdb +31 -0
  24. stcrpy/tcr_geometry/reference_data/reference_G.pdb +31 -0
  25. stcrpy/tcr_geometry/reference_data/reference_data.py +104 -0
  26. stcrpy/tcr_interactions/PLIPParser.py +147 -0
  27. stcrpy/tcr_interactions/TCRInteractionProfiler.py +433 -0
  28. stcrpy/tcr_interactions/TCRpMHC_PLIP_Model_Parser.py +133 -0
  29. stcrpy/tcr_interactions/__init__.py +0 -0
  30. stcrpy/tcr_interactions/utils.py +170 -0
  31. stcrpy/tcr_methods/__init__.py +0 -0
  32. stcrpy/tcr_methods/tcr_batch_operations.py +223 -0
  33. stcrpy/tcr_methods/tcr_methods.py +150 -0
  34. stcrpy/tcr_methods/tcr_reformatting.py +18 -0
  35. stcrpy/tcr_metrics/__init__.py +2 -0
  36. stcrpy/tcr_metrics/constants.py +39 -0
  37. stcrpy/tcr_metrics/tcr_interface_rmsd.py +237 -0
  38. stcrpy/tcr_metrics/tcr_rmsd.py +179 -0
  39. stcrpy/tcr_ml/__init__.py +0 -0
  40. stcrpy/tcr_ml/geometry_predictor.py +3 -0
  41. stcrpy/tcr_processing/AGchain.py +89 -0
  42. stcrpy/tcr_processing/Chemical_components.py +48915 -0
  43. stcrpy/tcr_processing/Entity.py +301 -0
  44. stcrpy/tcr_processing/Fragment.py +58 -0
  45. stcrpy/tcr_processing/Holder.py +24 -0
  46. stcrpy/tcr_processing/MHC.py +449 -0
  47. stcrpy/tcr_processing/MHCchain.py +149 -0
  48. stcrpy/tcr_processing/Model.py +37 -0
  49. stcrpy/tcr_processing/Select.py +145 -0
  50. stcrpy/tcr_processing/TCR.py +532 -0
  51. stcrpy/tcr_processing/TCRIO.py +47 -0
  52. stcrpy/tcr_processing/TCRParser.py +1230 -0
  53. stcrpy/tcr_processing/TCRStructure.py +148 -0
  54. stcrpy/tcr_processing/TCRchain.py +160 -0
  55. stcrpy/tcr_processing/__init__.py +3 -0
  56. stcrpy/tcr_processing/annotate.py +480 -0
  57. stcrpy/tcr_processing/utils/__init__.py +0 -0
  58. stcrpy/tcr_processing/utils/common.py +67 -0
  59. stcrpy/tcr_processing/utils/constants.py +367 -0
  60. stcrpy/tcr_processing/utils/region_definitions.py +782 -0
  61. stcrpy/utils/__init__.py +0 -0
  62. stcrpy/utils/error_stream.py +12 -0
  63. stcrpy-1.0.0.dist-info/METADATA +173 -0
  64. stcrpy-1.0.0.dist-info/RECORD +68 -0
  65. stcrpy-1.0.0.dist-info/WHEEL +5 -0
  66. stcrpy-1.0.0.dist-info/licenses/LICENCE +28 -0
  67. stcrpy-1.0.0.dist-info/licenses/stcrpy/tcr_geometry/TCRCoM_LICENCE +168 -0
  68. 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