forked from ecell/epdp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.py
409 lines (305 loc) · 12 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
import math
import numpy
import scipy
import myrandom
import _gfrd
Pi = scipy.pi
Pi2 = scipy.pi * 2.0
PiSqrt = math.sqrt(scipy.pi)
N_A = 6.0221367e23
INF = numpy.inf
ZEROPOS = numpy.array([0., 0., 0.])
NOWHERE = numpy.array((INF, INF, INF))
SAFETY = 1.0 + 1e-5
# Tolerance used for float comparison functions. Oversimplifying: two floats a
# and b are considered to be equal if abs(a - b) < TOLERANCE * abs(a).
TOLERANCE = 1e-7
# Multiplication factor used for seperating 2 particles or a particle and a
# surface after unbinding.
MINIMAL_SEPARATION_FACTOR = 1.0 + TOLERANCE
# Float comparison functions.
def feq(a, b, typical=1, tolerance=TOLERANCE):
# Return True if a and b are equal, subject to given tolerances.
# Float comparison.
# Also see numpy.allclose().
# The (relative) tolerance must be positive and << 1.0
# Instead of specifying an absolute tolerance, you can speciy a
# typical value for a or b. The absolute tolerance is then the
# relative tolerance multipied by this typical value, and will be
# used when comparing a value to zero. By default, the typical
# value is 1.
return abs(a - b) < tolerance * (typical + min(abs(a), abs(b)))
def fgreater(a, b, typical=1, tolerance=TOLERANCE):
# Return True if a is greater than b, subject to given tolerances.
# Float comparison.
return a - b > tolerance * (typical + min(abs(a), abs(b)))
def fless(a, b, typical=1, tolerance=TOLERANCE):
# Return True if a is less than b, subject to given tolerances.
# Float comparison.
return b - a > tolerance * (typical + min(abs(a), abs(b)))
def fgeq(a, b, typical=1, tolerance=TOLERANCE):
# Return True if a is greater or equal than b, subject to given
# tolerances. Float comparison.
diff = a - b
barrier = tolerance * (typical + min(abs(a), abs(b)))
# Try both 'greater than' and equality.
return diff > barrier or abs(diff) < barrier
def fleq(a, b, typical=1, tolerance=TOLERANCE):
# Return True if a is less than or equal than b, subject to given
# tolerances. Float comparison.
diff = b - a
barrier = tolerance * (typical + min(abs(a), abs(b)))
# Try both 'less than' and equality.
return diff > barrier or abs(diff) < barrier
def per_M_to_m3(rate):
"""Convert a reaction rate from units 'per molar per second' to
units 'meters^3 per second'.
"""
return rate / (1000 * N_A)
def per_microM_to_m3(rate):
"""Convert a reaction rate from units 'per micromolar per second' to
units 'meters^3 per second'.
"""
return per_M_to_m3(rate * 1e6)
def M_to_per_m3(molar):
"""Convert a concentration from units 'molar' to units 'per
meters^3'.
"""
return molar * (1000 * N_A)
def microM_to_per_m3(micromolar):
"""Convert a concentration from units 'micromolar' to units 'per
meters^3'.
"""
return M_to_per_m3(micromolar / 1e6)
def mean_arrival_time(r, D):
return (r * r) / (6.0 * D)
def uniq(l):
nset = {}
map(nset.__setitem__, l, [])
return nset.keys()
cyclic_transpose = _gfrd.cyclic_transpose
def distance_sq_array_simple(position1, positions, fsize = None):
return numpy.square(positions - position1).sum(1)
def distance_array_simple(position1, positions, fsize = None):
return numpy.sqrt(distance_sq_array_simple(position1, positions))
distance = _gfrd.distance
distance_cyclic = _gfrd.distance_cyclic
def distance_sq_array_cyclic(position1, positions, fsize):
diff = numpy.abs(positions - position1)
diff -= numpy.greater(diff, fsize * 0.5) * fsize # transpose
return numpy.square(diff).sum(1)
def distance_array_cyclic(position1, positions, fsize = 0):
return numpy.sqrt(distance_sq_array_cyclic(position1, positions, fsize))
def cartesian_to_spherical(c):
# x, y, z = c
r = length(c)
theta = math.acos(c[2] / r)
phi = math.atan2(c[1], c[0])
if phi < 0.0: # atan2 returns [- PI, PI]
phi += 2.0 * Pi
return numpy.array([r, theta, phi])
def spherical_to_cartesian(s):
#FIXME: it's possible that the below is a source of some bias.
r, theta, phi = s
sintheta = math.sin(theta)
return numpy.array([r * math.cos(phi) * sintheta,
r * math.sin(phi) * sintheta,
r * math.cos(theta)])
def random_unit_vector_s():
s = numpy.array([1.0, myrandom.uniform(0, Pi), myrandom.uniform(0, Pi2)])
return s
def random_unit_vector():
v = [myrandom.uniform(-1,1), myrandom.uniform(-1,1), myrandom.uniform(-1,1)]
return _gfrd.normalize(v, 1)
def random_vector(r):
# v, s = [0.0, 0.0, 0.0], 2.0
# while s > 1.0:
# v[0] = myrandom.uniform (-1, 1)
# v[1] = myrandom.uniform (-1, 1)
# s = v[0] * v[0] + v[1] * v[1]
# v[2] = -1 + 2 * s
# a = 2 * numpy.sqrt(1 - s)
# v[0] *= a
# v[1] *= a
cos_theta = myrandom.uniform(-1, 1)
sin_theta = numpy.sqrt(1 - cos_theta * cos_theta)
phi = myrandom.uniform(0, Pi2)
sin_phi, cos_phi = math.sin(phi), math.cos(phi) # sincos
v = [sin_theta * cos_phi, sin_theta * sin_phi, cos_theta]
return _gfrd.normalize(v, r)
def random_vector2D(r):
# Return a random 2D cartesian vector of length r.
phi = myrandom.uniform(0, Pi2)
v = [math.cos(phi), math.sin(phi)] # sincos
# Todo. return _gfrd.normalize(v, r)
v = numpy.array(v)
norm = numpy.linalg.norm(v)
return v * (r / norm)
def length(a):
return _gfrd.length(a)
def normalize(a, l=1):
return _gfrd.normalize(a, l)
def vector_angle(a, b):
cosangle = numpy.dot(a, b) / (length(a) * length(b))
return math.acos(cosangle)
def vector_angle_against_z_axis(b):
cosangle = b[2] / length(b)
return math.acos(cosangle)
def crossproduct(a, b):
M = numpy.array([[ 0.0, - a[2], a[1]],
[ a[2], 0.0, - a[0]],
[- a[1], a[0], 0.0]])
return numpy.dot(M, b)
def crossproduct_against_z_axis(a):
return numpy.array([- a[1], a[0], 0.0])
def rotate_vector(v, r, alpha):
# v: vector to rotate
# r: normalized rotation axis
# alpha: rotation angle in radian
cosalpha = math.cos(alpha)
sinalpha = math.sin(alpha)
cosalphac = 1.0 - cosalpha
M = numpy.array([[cosalpha + cosalphac * r[0] * r[0],
cosalphac * r[0] * r[1] - r[2] * sinalpha,
cosalphac * r[0] * r[2] + r[1] * sinalpha],
[cosalphac * r[0] * r[1] + r[2] * sinalpha,
cosalpha + cosalphac * r[1] * r[1],
cosalphac * r[1] * r[2] - r[0] * sinalpha],
[cosalphac * r[0] * r[2] - r[1] * sinalpha,
cosalphac * r[1] * r[2] + r[0] * sinalpha,
cosalpha + cosalphac * r[2] * r[2]]])
return numpy.dot(M,v)
def calculate_pair_CoM(pos1, pos2, D1, D2, world_size):
return _gfrd.calculate_pair_CoM(pos1, pos2, D1, D2, world_size);
apply_boundary = _gfrd.apply_boundary
def permutate(seq):
# permutate a sequence and return a list of the permutations
if not seq:
return [seq] # is an empty sequence
else:
temp = []
for k in range(len(seq)):
part = seq[:k] + seq[k+1:]
for m in permutate(part):
temp.append(seq[k:k+1] + m)
return temp
def k_D(Dtot, sigma):
"""Calculate the 'pseudo-'reaction rate (kD) caused by diffusion.
kD is equal to 1 divided by the time it takes for two particles to
meet each other by diffusion. It is needed when converting from
an intrinsic reaction rate to an overall reaction rates or vice
versa.
Example:
- A + B -> C.
Arguments:
- Dtot:
the diffusion constant of particle A plus the diffusion
constant of particle B. Units: meters^2/second.
- sigma
the radius of particle A plus the radius of particle B.
Units: meters.
This function is only available for reaction rules in 3D. No
analytical expression for kD in 1D or 2D is currently known.
"""
return 4.0 * numpy.pi * Dtot * sigma
def k_a(kon, kD):
"""Convert an overall reaction rate (kon) for a binding/annihilation
reaction rule to an intrinsic reaction rate (ka).
Example:
- A + B -> C
binding reaction rule
- A + B -> 0
annihilation reaction rule
Arguments:
- kon
the overall reaction rate for the reaction rule. Units:
meters^3/second.
- kD
the 'pseudo-'reaction rate caused by the diffusion of
particles A and B. See the function k_D(). Units:
meters^3/second.
This function is only available for reaction rules in 3D. No
analytical expression for kD in 1D or 2D is currently known.
"""
if kon > kD:
raise RuntimeError, 'kon > kD.'
ka = 1. / ((1. / kon) - (1. / kD))
return ka
def k_d(koff, kon, kD):
"""Convert an overall reaction rate (koff) for an unbinding reaction
rule to an intrinsic reaction rate (kd).
This one is a bit tricky. We consider reaction rules with only 1
reactant. In case there is only 1 product also, no conversion in
necessary. But when the reaction rule has 2 products, we need to
take the reverse reaction rule into account and do the proper
conversion.
Example:
- C -> A + B
unbinding reaction rule
- A + B -> C
reverse reaction rule
Arguments:
- koff
the overall reaction rate for the unbinding reaction rule.
Units: meters^3/second.
- kon
the overall reaction rate for the reverse reaction rule.
Units: meters^3/second.
- kD
the 'pseudo-'reaction rate caused by the diffusion of
particles A and B. See the function k_D(). Units:
meters^3/second.
This function is only available for reaction rules in 3D. No
analytical expression for kD in 1D or 2D is currently known.
"""
ka = k_a(kon, kD)
kd = k_d_using_ka(koff, ka, kD)
return kd
def k_d_using_ka(koff, ka, kD):
"""Convert an overall reaction rate (koff) for an unbinding reaction
rule to an intrinsic reaction rate (kd).
Similar to the function k_d(), but expects an intrinsic rate (ka)
instead of an overall rate (kon) for the reversed reaction rule as
the second argument.
This function is only available for reaction rules in 3D. No
analytical expression for kD in 1D or 2D is currently known.
"""
kd = koff * (1 + float(ka) / kD)
return kd
def k_on(ka, kD):
"""Convert an intrinsic reaction rate (ka) for a binding/annihilation
reaction rule to an overall reaction rate (kon).
The inverse of the function k_a().
Rarely needed.
This function is only available for reaction rules in 3D. No
analytical expression for kD in 1D or 2D is currently known.
"""
kon = 1. / ((1. / kD) + (1. / ka)) # m^3/s
return kon
def k_off(kd, kon, kD):
"""Convert an intrinsic reaction rate (kd) for an unbinding reaction
rule to an overall reaction rate (koff).
The inverse of the function k_d().
Rarely needed.
This function is only available for reaction rules in 3D. No
analytical expression for kD in 1D or 2D is currently known.
"""
ka = k_a(kon, kD)
koff = k_off_using_ka(kd, ka, kD)
return koff
def k_off_using_ka(kd, ka, kD):
"""Convert an intrinsic reaction rate (kd) for an unbinding reaction
rule to an overall reaction rate (koff).
Similar to the function k_off(), but expects an intrinsic rate
(ka) instead of an overall rate (kon) as the second argument.
Rarely needed.
This function is only available for reaction rules in 3D. No
analytical expression for kD in 1D or 2D is currently known.
"""
koff = 1. / (float(ka) / (kd * kD) + (1. / kd))
return koff
def C2N(c, V):
"""Calculate the number of particles in a volume 'V' (dm^3)
with a concentration 'c' (mol/dm^3).
"""
return c * V * N_A # round() here?