1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 from math import cos, sin
24 from numpy import array, dot, eye, float64, zeros
25
26
27 from lib.structure.angles import angles_regular, angles_uniform
28 from lib.structure.conversion import get_proton_name
29
30
31 -def generate_vector_dist(mol=None, res_name=None, res_num=None, chain_id='', centre=zeros(3, float64), R=eye(3), warp=eye(3), limit_check=None, scale=1.0, inc=20, distribution='uniform', debug=False):
32 """Generate a uniformly distributed distribution of atoms on a warped sphere.
33
34 The vectors from the function vect_dist_spherical_angles() are used to generate the distribution. These vectors are rotated to the desired frame using the rotation matrix 'R', then each compressed or stretched by the dot product with the 'warp' matrix. Each vector is centred and at the head of the vector, a proton is placed.
35
36
37 @keyword mol: The molecule container.
38 @type mol: MolContainer instance
39 @keyword res_name: The residue name.
40 @type res_name: str
41 @keyword res_num: The residue number.
42 @type res_num: int
43 @keyword chain_id: The chain identifier.
44 @type chain_id: str
45 @keyword centre: The centre of the distribution.
46 @type centre: numpy array, len 3
47 @keyword R: The optional 3x3 rotation matrix.
48 @type R: 3x3 numpy array
49 @keyword warp: The optional 3x3 warping matrix.
50 @type warp: 3x3 numpy array
51 @keyword limit_check: A function with determines the limits of the distribution. It should accept two arguments, the polar angle phi and the azimuthal angle theta, and return True if the point is in the limits or False if outside.
52 @type limit_check: function
53 @keyword scale: The scaling factor to stretch all rotated and warped vectors by.
54 @type scale: float
55 @keyword inc: The number of increments or number of vectors.
56 @type inc: int
57 @keyword distribution: The type of point distribution to use. This can be 'uniform' or 'regular'.
58 @type distribution: str
59 """
60
61
62 if len(mol.atom_num) == 0:
63 origin_num = 1
64 else:
65 origin_num = mol.atom_num[-1]+1
66 atom_num = origin_num
67
68
69 print(" Creating the uniform vector distribution.")
70 vectors = vect_dist_spherical_angles(inc=inc, distribution=distribution)
71
72
73 if distribution == 'uniform':
74 phi, theta = angles_uniform(inc)
75 else:
76 phi, theta = angles_regular(inc)
77
78
79 edge = zeros(len(theta))
80 edge_index = zeros(len(theta), int)
81 edge_phi = zeros(len(theta), float64)
82 edge_atom = zeros(len(theta))
83
84
85 for i in range(len(theta)):
86
87 if debug:
88 print("i: %s; theta: %s" % (i, theta[i]))
89
90
91 for j in range(len(phi)):
92
93 if debug:
94 print("%sj: %s; phi: %s" % (" "*4, j, phi[j]))
95
96
97 if limit_check and not limit_check(phi[j], theta[i]):
98 if debug:
99 print("%sOut of limits." % (" "*8))
100 continue
101
102
103 if not edge[i]:
104 edge[i] = 1
105 edge_index[i] = j
106 edge_phi[i] = phi[j]
107 edge_atom[i] = atom_num
108
109
110 if debug:
111 print("%sEdge detected." % (" "*8))
112 print("%sEdge index: %s" % (" "*8, edge_index[i]))
113 print("%sEdge phi pos: %s" % (" "*8, edge_phi[i]))
114 print("%sEdge atom: %s" % (" "*8, edge_atom[i]))
115
116
117 vector = dot(R, vectors[i + j*len(theta)])
118
119
120 vector = dot(warp, vector)
121
122
123 vector = vector * scale
124
125
126 pos = centre + vector
127
128
129 if debug:
130 print("%sAdding atom %s." % (" "*8, get_proton_name(atom_num)))
131
132
133 mol.atom_add(pdb_record='HETATM', atom_num=atom_num, atom_name=get_proton_name(atom_num), res_name=res_name, chain_id=chain_id, res_num=res_num, pos=pos, segment_id=None, element='H')
134
135
136 if j > edge_index[i]:
137
138 if debug:
139 print("%sLongitude line, connecting %s to %s" % (" "*8, get_proton_name(atom_num), get_proton_name(atom_num-1)))
140
141 mol.atom_connect(index1=atom_num-1, index2=atom_num-2)
142
143
144 if i != 0 and j >= edge_index[i-1]:
145
146 num = len(phi) - edge_index[i]
147
148
149 if debug:
150 print("%sLatitude line, connecting %s to %s" % (" "*8, get_proton_name(atom_num), get_proton_name(atom_num-num)))
151
152 mol.atom_connect(index1=atom_num-1, index2=atom_num-1-num)
153
154
155 if i == len(theta)-1 and j >= edge_index[0]:
156
157 num = origin_num + j - edge_index[0]
158
159
160 if debug:
161 print("%sZipping up, connecting %s to %s" % (" "*8, get_proton_name(atom_num), get_proton_name(num)))
162
163 mol.atom_connect(index1=atom_num-1, index2=num-1)
164
165
166 atom_num = atom_num + 1
167
168
169 -def generate_vector_residues(mol=None, vector=None, atom_name=None, res_name_vect='AXS', sim_vectors=None, res_name_sim='SIM', chain_id='', res_num=None, origin=None, scale=1.0, label_placement=1.1, neg=False):
170 """Generate residue representations for the vector and the MC simulationed vectors.
171
172 This is used to create a PDB representation of any vector, including its Monte Carlo simulations.
173
174 @keyword mol: The molecule container.
175 @type mol: MolContainer instance
176 @keyword vector: The vector to be represented in the PDB.
177 @type vector: numpy array, len 3
178 @keyword atom_name: The atom name used to label the atom representing the head of the vector.
179 @type atom_name: str
180 @keyword res_name_vect: The 3 letter PDB residue code used to represent the vector.
181 @type res_name_vect: str
182 @keyword sim_vectors: The optional Monte Carlo simulation vectors to be represented in the PDB.
183 @type sim_vectors: list of numpy array, each len 3
184 @keyword res_name_sim: The 3 letter PDB residue code used to represent the Monte Carlo simulation vectors.
185 @type res_name_sim: str
186 @keyword chain_id: The chain identification code.
187 @type chain_id: str
188 @keyword res_num: The residue number.
189 @type res_num: int
190 @keyword origin: The origin for the axis.
191 @type origin: numpy array, len 3
192 @keyword scale: The scaling factor to stretch the vectors by.
193 @type scale: float
194 @keyword label_placement: A scaling factor to multiply the pre-scaled vector by. This is used to place the vector labels a little further out from the vector itself.
195 @type label_placement: float
196 @keyword neg: If True, then the negative vector positioned at the origin will also be included.
197 @type neg: bool
198 @return: The new residue number.
199 @rtype: int
200 """
201
202
203 origin_num = len(mol.atom_num)+1
204 atom_num = len(mol.atom_num)+2
205 atom_neg_num = len(mol.atom_num)+3
206
207
208 mol.atom_add(pdb_record='HETATM', atom_num=origin_num, atom_name='R', res_name=res_name_vect, chain_id=chain_id, res_num=res_num, pos=origin, segment_id=None, element='C')
209
210
211 mol.atom_add(pdb_record='HETATM', atom_num=atom_num, atom_name=atom_name, res_name=res_name_vect, chain_id=chain_id, res_num=res_num, pos=origin+vector*scale, segment_id=None, element='C')
212 mol.atom_connect(index1=atom_num-1, index2=origin_num-1)
213 num = 1
214 if neg:
215 mol.atom_add(pdb_record='HETATM', atom_num=atom_neg_num, atom_name=atom_name, res_name=res_name_vect, chain_id=chain_id, res_num=res_num, pos=origin-vector*scale, segment_id=None, element='C')
216 mol.atom_connect(index1=atom_neg_num-1, index2=origin_num-1)
217 num = 2
218
219
220 mol.atom_add(pdb_record='HETATM', atom_num=atom_num+num, atom_name=atom_name, res_name=res_name_vect, chain_id=chain_id, res_num=res_num, pos=origin+label_placement*vector*scale, segment_id=None, element='N')
221 if neg:
222 mol.atom_add(pdb_record='HETATM', atom_num=atom_neg_num+num, atom_name=atom_name, res_name=res_name_vect, chain_id=chain_id, res_num=res_num, pos=origin-label_placement*vector*scale, segment_id=None, element='N')
223
224
225 print(" " + atom_name + " vector (scaled + shifted to origin): " + repr(origin+vector*scale))
226 print(" Creating the MC simulation vectors.")
227
228
229 if sim_vectors != None:
230 for i in range(len(sim_vectors)):
231
232 res_num = res_num + 1
233
234
235 atom_num = mol.atom_num[-1]+1
236 atom_neg_num = mol.atom_num[-1]+2
237
238
239 mol.atom_add(pdb_record='HETATM', atom_num=atom_num, atom_name=atom_name, res_name=res_name_sim, chain_id=chain_id, res_num=res_num, pos=origin+sim_vectors[i]*scale, segment_id=None, element='C')
240 mol.atom_connect(index1=atom_num-1, index2=origin_num-1)
241 if neg:
242 mol.atom_add(pdb_record='HETATM', atom_num=atom_num+1, atom_name=atom_name, res_name=res_name_sim, chain_id=chain_id, res_num=res_num, pos=origin-sim_vectors[i]*scale, segment_id=None, element='C')
243 mol.atom_connect(index1=atom_neg_num-1, index2=origin_num-1)
244
245
246 return res_num
247
248
250 """Create a distribution of vectors on a sphere using a distribution of spherical angles.
251
252 This function returns an array of unit vectors distributed within 3D space. The unit vectors are generated using the equation::
253
254 | cos(theta) * sin(phi) |
255 vector = | sin(theta) * sin(phi) |.
256 | cos(phi) |
257
258 The vectors of this distribution generate both longitudinal and latitudinal lines.
259
260
261 @keyword inc: The number of increments in the distribution.
262 @type inc: int
263 @keyword distribution: The type of point distribution to use. This can be 'uniform' or 'regular'.
264 @type distribution: str
265 @return: The distribution of vectors on a sphere.
266 @rtype: list of rank-1, 3D numpy arrays
267 """
268
269
270 if distribution == 'uniform':
271 phi, theta = angles_uniform(inc)
272 else:
273 phi, theta = angles_regular(inc)
274
275
276 vectors = []
277
278
279 for j in range(len(phi)):
280
281 for i in range(len(theta)):
282
283 x = cos(theta[i]) * sin(phi[j])
284
285
286 y = sin(theta[i])* sin(phi[j])
287
288
289 z = cos(phi[j])
290
291
292 vectors.append(array([x, y, z], float64))
293
294
295 return vectors
296