-
Notifications
You must be signed in to change notification settings - Fork 3
/
calc_velocity.py
256 lines (207 loc) · 8.31 KB
/
calc_velocity.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
# -*- coding: utf-8 -*-
"""
Created on Wed Apr 9 15:39:28 2014
@author: ibackus
"""
__version__ = "$Revision: 1 $"
# $Source$
import numpy as np
import pynbody
SimArray = pynbody.array.SimArray
import isaac
import ICgen_utils
import os
import glob
import gc
def v_xy(f, param, changbin=None, nr=50, min_per_bin=100, changa_preset=None, \
max_particles=None, est_eps=True):
"""
Attempts to calculate the circular velocities for particles in a thin
(not flat) keplerian disk. Also estimates gravitational softening (eps)
for the gas particles
Requires ChaNGa
Note that this will change the velocities IN f
**ARGUMENTS**
f : tipsy snapshot
For a gaseous disk
param : dict
a dictionary containing params for changa. (see isaac.configparser)
changbin : str (OPTIONAL)
If set, should be the full path to the ChaNGa executable. If None,
an attempt to find ChaNGa is made
nr : int (optional)
number of radial bins to use when averaging over accelerations
min_per_bin : int (optional)
The minimum number of particles to be in each bin. If there are too
few particles in a bin, it is merged with an adjacent bin. Thus,
actual number of radial bins may be less than nr.
changa_preset : str
Which ChaNGa execution preset to use (ie 'mpi', 'local', ...). See
ICgen_utils.changa_command
max_particles : int or None
Specifies the maximum number of particles to use for calculating
accelerations and velocities. Setting a smaller number can speed up
computation and save on memory but can yield noisier results.
If None, max is unlimited.
est_eps : bool
Estimate eps (gravitational softening length). Default is True.
If False, it is assumed eps has already been estimated
**RETURNS**
Nothing. Velocities are updated within f as is eps
"""
# If the snapshot has too many particles, randomly select gas particles
# To use for calculating velocity and make a view of the snapshot
n_gas = len(f) - 1
subview = (n_gas > max_particles) and (max_particles is not None)
if subview:
max_particles = int(max_particles)
mask = np.zeros(n_gas + 1, dtype=bool)
mask[-1] = True # Use the star particle always
# randomly select particles to use
m = np.random.rand(n_gas)
ind = m.argsort()[0:max_particles]
mask[ind] = True
# Make a subview and create a reference to the complete snapshot
complete_snapshot = f
f = complete_snapshot[mask]
# Scale gas mass
m_scale = float(n_gas)/float(max_particles)
f.g['mass'] *= m_scale
if not est_eps:
f.g['eps'] *= m_scale**(1.0/3)
# Load stuff from the snapshot
r = f.g['rxy'].astype(np.float32)
cosine = (f.g['x']/r).in_units('1').astype(np.float32)
sine = (f.g['y']/r).in_units('1').astype(np.float32)
z = f.g['z']
vel = f.g['vel']
a = None # arbitrary initialization
# Temporary filenames for running ChaNGa
f_prefix = str(np.random.randint(0, 2**32))
f_name = f_prefix + '.std'
p_name = f_prefix + '.param'
# Update parameters
p_temp = param.copy()
p_temp['achInFile'] = f_name
p_temp['achOutName'] = f_prefix
p_temp['dDelta'] = 1e-10
if 'dDumpFrameTime' in p_temp: p_temp.pop('dDumpFrameTime')
if 'dDumpFrameStep' in p_temp: p_temp.pop('dDumpFrameStep')
# --------------------------------------------
# Estimate velocity from gravity only
# --------------------------------------------
for iGrav in range(2):
# Save files
f.write(filename=f_name, fmt = pynbody.tipsy.TipsySnap)
isaac.configsave(p_temp, p_name, ftype='param')
if iGrav == 0:
# Run ChaNGa calculating all forces (for initial run)
command = ICgen_utils.changa_command(p_name, changa_preset, changbin, '+gas +n 0')
else:
# Run ChaNGa, only calculating gravity (on second run)
command = ICgen_utils.changa_command(p_name, changa_preset, changbin, '-gas +n 0')
print command
p = ICgen_utils.changa_run(command)
p.wait()
if (iGrav == 0) and est_eps:
# Estimate the gravitational softening length on the first iteration
smoothlength_file = f_prefix + '.000000.smoothlength'
eps = ICgen_utils.est_eps(smoothlength_file)
f.g['eps'] = eps
# Load accelerations
acc_name = f_prefix + '.000000.acc2'
del a
gc.collect()
a = isaac.load_acc(acc_name, low_mem=True)
gc.collect()
# Clean-up
for fname in glob.glob(f_prefix + '*'): os.remove(fname)
# Calculate cos(theta) where theta is angle above x-y plane
cos = (r/np.sqrt(r**2 + z**2)).in_units('1').astype(np.float32)
# Calculate radial acceleration times r^2
ar2 = (a[:,0]*cosine + a[:,1]*sine)*r**2
# Bin the data
r_edges = np.linspace(r.min(), (1+np.spacing(2))*r.max(), nr + 1)
ind, r_edges = isaac.digitize_threshold(r, min_per_bin, r_edges)
ind -= 1
nr = len(r_edges) - 1
r_bins, ar2_mean, err = isaac.binned_mean(r, ar2, binedges=r_edges, \
weighted_bins=True)
gc.collect()
# Fit lines to ar2 vs cos for each radial bin
m = np.zeros(nr)
b = np.zeros(nr)
for i in range(nr):
mask = (ind == i)
p = np.polyfit(cos[mask], ar2[mask], 1)
m[i] = p[0]
b[i] = p[1]
# Interpolate the line fits
m_spline = isaac.extrap1d(r_bins, m)
b_spline = isaac.extrap1d(r_bins, b)
# Calculate circular velocity
ar2 = SimArray(m_spline(r)*cos + b_spline(r), ar2.units)
gc.collect()
v_calc = (np.sqrt(abs(ar2)/r)).in_units(vel.units)
gc.collect()
vel[:,0] = -v_calc*sine
vel[:,1] = v_calc*cosine
del v_calc
gc.collect()
# --------------------------------------------
# Estimate pressure/gas dynamics accelerations
# --------------------------------------------
# Save files
f.write(filename=f_name, fmt = pynbody.tipsy.TipsySnap)
isaac.configsave(p_temp, p_name, ftype='param')
# Run ChaNGa, including SPH
command = ICgen_utils.changa_command(p_name, changa_preset, changbin, '+gas -n 0')
p = ICgen_utils.changa_run(command)
p.wait()
# Load accelerations
acc_name = f_prefix + '.000000.acc2'
a_total = isaac.load_acc(acc_name, low_mem=True)
gc.collect()
# Clean-up
for fname in glob.glob(f_prefix + '*'): os.remove(fname)
# Estimate the accelerations due to pressure gradients/gas dynamics
a_gas = a_total - a
del a_total, a
gc.collect()
ar2_gas = (a_gas[:,0]*cosine + a_gas[:,1]*sine)*r**2
del a_gas
gc.collect()
logr_bins, ratio, err = isaac.binned_mean(np.log(r), ar2_gas/ar2, nbins=nr,\
weighted_bins=True)
r_bins = np.exp(logr_bins)
del ar2_gas
gc.collect()
ratio_spline = isaac.extrap1d(r_bins, ratio)
# If not all the particles were used for calculating velocity,
# Make sure to use them now
if subview:
# Re-scale mass back to normal
f.g['mass'] /= m_scale
# Scale eps appropriately
f.g['eps'] /= m_scale**(1.0/3)
complete_snapshot.g['eps'] = f.g['eps'][[0]]
# Rename complete snapshot
f = complete_snapshot
# Calculate stuff for all particles
r = f.g['rxy']
z = f.g['z']
cos = (r/np.sqrt(r**2 + z**2)).in_units('1').astype(np.float32)
ar2 = SimArray(m_spline(r)*cos + b_spline(r), ar2.units)
cosine = (f.g['x']/r).in_units('1').astype(np.float32)
sine = (f.g['y']/r).in_units('1').astype(np.float32)
vel = f.g['vel']
ar2_calc = ar2*(1 + ratio_spline(r))
del ar2
gc.collect()
# Calculate velocity
v = (np.sqrt(abs(ar2_calc)/r)).in_units(f.g['vel'].units)
del ar2_calc
gc.collect()
vel[:,0] = -v*sine
vel[:,1] = v*cosine
return