-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHelpers.py
365 lines (289 loc) · 12.9 KB
/
Helpers.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
#!/usr/bin/env python3
import numpy as np
from numpy.core.umath_tests import matrix_multiply as _matrix_multiply
# TODO: Write unit tests for all of these helper functions.
def SphPosToCart(vectors, radians=False):
"""Convert a spherical position vector into Cartesian position.
Arguments:
vector -- A 3-element NumPy array representing, in order,
the spherical r, theta, and phi position coordinates.
Returns a 3-element NumPy array representing, in order, the
representative Cartesian x, y, and z coordinates of the input vector.
"""
if vectors.ndim == 1:
if len(vectors) != 3:
print("ERROR - SphPosToCart(): Vector not a 3-dimensional vector! Aborting.")
return
elif vectors.ndim > 2:
print("ERROR - SphPosToCart(): Only handles a list of 3D vectors \
(2-dimensional array). Aborting.")
return
if vectors.ndim == 1:
r, theta, phi = vectors
elif vectors.ndim == 2:
r = vectors[:,0]
theta = vectors[:,1]
phi = vectors[:,2]
if not radians:
theta = np.radians(theta % 360)
phi = np.radians(phi % 360)
result = np.array([r * np.sin(theta) * np.cos(phi),
r * np.sin(theta) * np.sin(phi),
r * np.cos(theta)])
# Transpose only has an effect for arrays of vectors, but puts vectors into
# rows not columns, the way it should be.
return result.T
def CartPosToSph(vectors, radians=False):
"""Convert a Cartesian position vector into spherical coordinate space.
NOTE: Largely untested...
Arguments:
vectors -- A 3-element NumPy array representing, in order,
Cartesian x, y, and z position coordinates.
Returns a 3-element NumPy array representing, in order, the
spherical r, theta, and phi position coordinates of the input vector.
"""
if vectors.ndim == 1:
if len(vectors) != 3:
print("ERROR - CartPosToSph(): Vector not a 3-dimensional vector! Aborting.")
return
elif vectors.ndim > 2:
print("ERROR - CartPosToSph(): Only handles a list of 3D vectors \
(2-dimensional array). Aborting.")
return
if vectors.ndim == 1:
x, y, z = vectors
elif vectors.ndim == 2:
x = vectors[:,0]
y = vectors[:,1]
z = vectors[:,2]
r = np.sqrt(x**2+y**2+z**2)
theta = np.arctan2(np.sqrt(x**2+y**2), z)
phi = np.arctan2(y, x)
if not radians:
theta = np.degrees(theta % (2*np.pi))
phi = np.degrees(phi % (2*np.pi))
result = np.array([r, theta, phi])
# Transpose only has an effect for arrays of vectors, but puts vectors into
# rows not columns, the way it should be.
return result.T
def SphVecToCart(position, vector, radians=False):
"""Convert spherical vectors into Cartesian vector space.
Takes spherical-space vectors and their corresponding spherical-
space positions and returns the magnitude of the vectors in x, y,
and z Cartesian directions.
Arguments:
position -- A 3-element NumPy array, or array of arrays, representing
the position of the vector(s) in spherical space.
vector -- A 3-element NumPy array, or array of arrays, representing
the vector(s) to be converted, also in spherical space.
Returns a 3-element NumPy array representing the magnitude of
the input vector in Cartesian vector space.
"""
if len(position) != len(vector):
print("ERROR - SphVecToCart(): \
Vector and position arrays must have the same length! Aborting.")
return
if position.ndim == 1 and vector.ndim == 1:
if len(position) == 3:
r, theta, phi = position
else:
print("ERROR - SphVecToCart(): \
Vectors and positions must each have three elements! Aborting.")
return
elif position.ndim == 2 and vector.ndim == 2:
# Maybe an error-checking thing for 3-element vectors like above?
r, theta, phi = position[:,[0,1,2]].T
else:
print("ERROR - SphVecToCart(): \
Vector and position arrays must have the same dimensions, or must \
be either 1D or 2D arrays! Aborting.")
return
if not radians:
theta = np.radians(theta % 360)
phi = np.radians(phi % 360)
# Calculating x-hat, y-hat, and z-hat from r-hat, theta-hat, and phi-hat
transform_matrix = np.array([
[np.sin(theta) * np.cos(phi), np.sin(theta) * np.sin(phi), np.cos(theta)],
[np.cos(theta) * np.cos(phi), np.cos(theta) * np.sin(phi),-np.sin(theta)],
[-np.sin(phi), np.cos(phi), np.zeros_like(theta)]
])
# Do the dot products!
return np.squeeze(_matrix_multiply(transform_matrix.T, vector[...,None]))
def RotX(vector, angle, radians=False):
"""Rotate a Cartesian vector by a given angle about the +x axis.
NOTE: Probably needs to be re-written (to be like Rot()) for accepting
arrays of vectors.
This function rotates a given vector about the Cartesian +x axis.
The rotation is in a right-handed sense; positive angles rotate
from the +y axis toward the +z axis.
Arguments:
vector -- A 3-element NumPy array to be rotated.
angle -- The angle by which the input vector will be rotated.
Returns a 3-element NumPy array representing the rotated vector.
"""
if not radians:
angle = np.radians(angle % 360)
R_X = np.array([[1, 0, 0],
[0, np.cos(angle), -np.sin(angle)],
[0, np.sin(angle), np.cos(angle)]])
return R_X.dot(vector)
def RotY(vector, angle, radians=False):
"""Rotate a Cartesian vector by a given angle about the +y axis.
NOTE: Probably needs to be re-written (to be like Rot()) for accepting
arrays of vectors.
This function rotates a given vector about the Cartesian +y axis.
The rotation is in a right-handed sense; positive angles rotate
from the +z axis toward the +x axis.
Arguments:
vector -- A 3-element NumPy array to be rotated.
angle -- The angle by which the input vector will be rotated.
Keywords:
radians -- Whether 'angle' is in radians (True) or degrees (False; default).
Returns a 3-element NumPy array representing the rotated vector.
"""
if not radians:
angle = np.radians(angle % 360)
R_Y = np.array([[ np.cos(angle), 0, np.sin(angle)],
[ 0, 1, 0],
[-np.sin(angle), 0, np.cos(angle)]])
return R_Y.dot(vector)
def RotZ(vector, angle, radians=False):
"""Rotate a Cartesian vector by a given angle about the +z axis.
NOTE: Probably needs to be re-written (to be like Rot()) for accepting
arrays of vectors.
This function rotates a given vector about the Cartesian +z axis.
The rotation is in a right-handed sense; positive angles rotate
from the +x axis toward the +y axis.
Arguments:
vector -- A 3-element NumPy array to be rotated.
angle -- The angle by which the input vector will be rotated.
Keywords:
radians -- Whether 'angle' is in radians (True) or degrees (False; default).
Returns a 3-element NumPy array representing the rotated vector.
"""
if not radians:
angle = np.radians(angle % 360)
R_Z = np.array([[ np.cos(angle), -np.sin(angle), 0],
[ np.sin(angle), np.cos(angle), 0],
[ 0, 0, 1]])
return R_Z.dot(vector)
def Rot(vectors, x=0., y=0., z=0., radians=False):
"""Rotate Cartesian vectors.
This function rotates input vectors about the Cartesian axes.
The rotation is in a right-handed sense.
Arguments:
vector -- A NumPy array of vectors to be rotated.
Keywords:
x -- The angle to rotate about the x-axis.
y -- The angle to rotate about the y-axis.
z -- The angle to rotate about the z-axis.
radians -- Whether the above angles are given in radians (True)
or degrees (False; default).
Returns a NumPy array representing the rotated vectors.
"""
if vectors.ndim == 1:
if len(vectors) != 3:
print("ERROR - Rot(): Vector not a 3-dimensional vector! Aborting.")
return
elif vectors.ndim > 2:
print("ERROR - Rot(): Only handles a list of 3D vectors (2-dimensional array). \
Aborting.")
return
if not radians:
x = np.radians(x % 360)
y = np.radians(y % 360)
z = np.radians(z % 360)
R_X = np.matrix([[ 1, 0, 0],
[ 0, np.cos(x), -np.sin(x)],
[ 0, np.sin(x), np.cos(x)]])
R_Y = np.matrix([[ np.cos(y), 0, np.sin(y)],
[ 0, 1, 0],
[-np.sin(y), 0, np.cos(y)]])
R_Z = np.matrix([[ np.cos(z), -np.sin(z), 0],
[ np.sin(z), np.cos(z), 0],
[ 0, 0, 1]])
R = R_Z * R_Y * R_X
if vectors.ndim == 1: # A single vector
result = R.dot(vectors).A1 # Return result as flattened array.
elif vectors.ndim == 2: # A list of vectors
result = R.dot(vectors.T).T.A
return result
def CylPosToCart(vector):
"""Convert a cylindrical position vector into Cartesian position.
NOTE: Haven't really used this, so it might not be great.
Arguments:
vector -- A 3-element NumPy array representing, in order,
the cylindrical r, theta, and z position coordinates.
Returns a 3-element NumPy array representing, in order, the
representative Cartesian x, y, and z coordinates of the input vector.
"""
if len(vector) != 3:
print("WARNING - CylPosToCart(): Not a 3-dimensional vector!")
r, phi, z = vector
phi = np.radians(phi)
return np.array([r * np.cos(phi),
r * np.sin(phi),
z])
def CartPosToCyl(vector):
"""Convert a Cartesian position vector into cylindrical coordinates.
NOTE: Haven't really used this, so it might not be great.
Arguments:
vector -- A 3-element NumPy array representing, in order,
Cartesian x, y, and z position coordinates.
Returns a 3-element NumPy array representing, in order, the
cylindrical r, theta, and z position coordinates of the input vector.
"""
if len(vector) != 3:
print("WARNING - CartPosToCyl(): Not a 3-dimensional vector!")
x, y, z = vector[:3]
r = np.sqrt(x**2+y**2)
phi = np.degrees(np.arctan2(y, x))
return np.array([r, phi, z])
def CylVecToCart(position, vector):
"""Convert a cylindrical vector into Cartesian vector space.
N.B.: Not optimized! See SphVecToCart() for a better way to do this.
Takes a cylindrical-space vector and its corresponding cylindrical-
space position and returns the magnitude of the vector in x, y,
and z Cartesian directions.
Arguments:
position -- A 3-element NumPy array, representing the position
of the vector in cylindrical space.
vector -- A 3-element NumPy array, representing the vector to
be converted, also in cylindrical space.
Returns a 3-element NumPy array representing the magnitude of
the input vector in Cartesian vector space.
"""
if len(position) != 3:
print("WARNING - CylVecToCart(): Position not a 3-dimensional vector!")
if len(vector) != 3:
print("WARNING - CylVecToCart(): Vector not a 3-dimensional vector!")
r, phi, z = position
phi = np.radians(phi % 360)
r_hat = np.array([np.cos(phi), #x_hat
np.sin(phi), #y_hat
0]) #z_hat
phi_hat = np.array([-np.sin(phi), #x_hat
np.cos(phi), #y_hat
0]) #z_hat
z_hat = np.array([0, #x_hat
0, #y_hat
1]) #z_hat
transform_matrix = np.array([r_hat, phi_hat, z_hat])
return vector.dot(transform_matrix)
def RotCurve(vel, radius, C=0.3, p=1.35):
"""Create an analytic disk galaxy rotation curve.
Arguments:
vel -- The approximate maximum circular velocity.
radius -- The radius (or radii) at which to calculate the
rotation curve.
Keywords:
C -- Controls the radius at which the curve turns over,
in the same units as 'radius'.
p -- Controls the fall-off of the curve after the turn-over;
values expected to be between 1 and 1.5 for disks.
Returns the value of the rotation curve at the given radius.
See Bertola et al. 1991, ApJ, 373, 369 for more information.
"""
C_ = C # kpc
p_ = p
return vel * radius / ((radius**2 + C_**2)**(p_/2.))