-
Notifications
You must be signed in to change notification settings - Fork 4
/
loader_readsnap.py
315 lines (255 loc) · 9.4 KB
/
loader_readsnap.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
import os,sys
from readsnap import readsnap
import np_tools
import numpy as np
def verifyLoader(loader):
try:
loader.importParticleData
loader.getPointcloud
loader.setDataMode
except NameError:
raise Exception("Invalid Loader")
def addDimensionToPC(self,key,value):
self.dataset.l.addDimension(key,value)
dim = self.dataset.addDimension(key,DimensionType.Float,0,key.lower()+'_dsdim')
try:
self.dims+=[dim]
except AttributeError:
self.dims=[dim]
dim.__class__.__repr__=lambda self: self.label
return dim
class Loader(object):
"""Loader class that organizes required parts of loader script
for verification. Standardizes class format for any additional loader classes."""
def __init__(self,snapdir,snapnum):
"""
Saves the arguments to the structure.
Input:
datasetBase - path to the dataset
snapshotNumber - which snapshot we want
"""
self.snapdir=snapdir
self.snapnum=snapnum
def importParticleData(self,ptype):
"""
Reads data in from whatever format you'd like.
Input:
ptype - The numerical index of the particle type you want
Output:
res - A dictionary that contains information for a *single* pointcloud!
"""
#we have a function of exactly this format, how convenient (/sarcasm)!
return readsnap(self.snapdir,self.snapnum,ptype)
def getPointcloud(self,res,keys,color,lowres=0):
"""
Creates a pointcloud object out of a particle dictionary, res.
Input:
res - the dictionary containing information for the pointcloud you're creating
keys - the keys that are relevant for your res dictionary that you want to load
into a dataset object for future use. This *shouldn't* be all the keys in res,
in general, just the ones you anticipate needing.
color - the color of the points when no advanced colormap is used. This is also
used to indentify the pointclouds, so they should *not* overlap (if you think
they should they shouldn't-- you should merge your data to be a single point
cloud instead of having two representing subsets of data that should be the same
color).
Output:
pc - a pointcloud object
"""
l = NumpyLoader()
#every dataset is called parttype0
ds = Dataset.create('PartType0')
#tell dataset where to get its info from
ds.setLoader(l)
ds.l = l
#wrapper point cloud that adds a method above
pc = PointCloud.create('pc%s'%color)
pc.res = res
pc.loader = self
pc.dataset = ds
#make it a bound method...
pc.addDimensionToPC=addDimensionToPC.__get__(pc,pc.__class__)
#assert that whatever you're loading has spatial coordinates
#and spatial velocities?
try:
assert 'p' in keys
assert 'v' in keys
except AssertionError:
print keys
raise Exception("You need to specify the 3d positions using the 'p' key")
l.addDimension('Coordinates',res['p'])
l.addDimension('Velocities',res['v'])
# 1-> array to get, 2-> should almost always be float,
# 3-> index for vector variables (0 if single dimensional)
# 4->potentially another arbitrary label that is legacy
pc.x_dim = ds.addDimension('Coordinates', DimensionType.Float,0,'x')
pc.y_dim = ds.addDimension('Coordinates', DimensionType.Float, 1, 'y')
pc.z_dim = ds.addDimension('Coordinates', DimensionType.Float, 2, 'z')
pc.vx_dim = ds.addDimension('Velocities', DimensionType.Float, 0, 'vx')
pc.vy_dim = ds.addDimension('Velocities', DimensionType.Float, 1, 'vy')
pc.vz_dim = ds.addDimension('Velocities', DimensionType.Float, 2, 'vz')
if 'rho' in keys:
radius = np.power(((res['m'] / res['rho'])/(2 * np.pi) ),(1.0/3.0))
pc.gas_sd = 2 * radius * res['rho']
self.orientCamera(res,pc.gas_sd,radius)
pc.gas_sd_dim = pc.addDimensionToPC('SurfaceDensity',pc.gas_sd)
pc.gas_r_dim = pc.addDimensionToPC('ParticleRadius',radius)
#metal surface density
pc.gas_met_sd = 2 * radius * res['rho']*res['z'][:,0]
pc.gas_met_sd_dim = pc.addDimensionToPC('MetalSurfaceDensity',pc.gas_met_sd)
#sfr
pc.gas_sfr = res['sfr']
pc.gas_sfr_dim = pc.addDimensionToPC('GasSFR',pc.gas_sfr)
pc.is_gas_particles = True
else:
shape = res['m'].shape
#track dummy arrays just in case
pc.gas_sd_dim = pc.addDimensionToPC('SurfaceDensityDummy',np.zeros(shape=shape))
pc.gas_r_dim = pc.addDimensionToPC('ParticleRadiusDummy',np.ones(shape=shape))
pc.is_gas_particles = False
if lowres:
pointCloudLoadOptions = "50000 0:100000:100"
else:
pointCloudLoadOptions = "50000 0:100000:20"
pc.setOptions(pointCloudLoadOptions)
#this is very different usage of dimension from above, this
#sets x,y,z coordinates in sim
pc.setDimensions(pc.x_dim,pc.y_dim,pc.z_dim)
#can also take an RGBA string, used if no adv cmap
pc.setColor(Color(color))
pc.setPointScale(pointScale)
return pc
def setDataMode(self,mode):
"""mode is an index and is exported to the
global variable dataMode, then used to pick
out from list above"""
global dataMode,dataModes
#default will be the first that appears in the list
dataModes = ['Point','Surface Density','Metal Surface Density','SFR','Gas Velocities']
#why are these lines here?
## stores index in global variable
dataMode = mode
## extracts index once for lines below...
dm = dataModes[mode]
#go through each possible
## should consider replacing dataModes with a list of functions
## that takes parts as an argument...
if dm not in ['SFR']:
# certain datamodes look better if you change the decimation
#SFR is one of them, want to reset it in case you were just in
#one of them.
setDecimationValue(10)
pass
if dm =='Surface Density':
for pci,pc in enumerate(parts):
if pc.is_gas_particles:
pc.setProgram(prog_channel)
pc.setSize(None)
pc.setVisible(True)
pc.setData(pc.gas_sd_dim)
#update colormap bounds to 5 sigma in either logspace or not
updateColormapBounds(*np_tools.setDefaultRanges(pc.gas_sd))
else:
#pc.setData(None)
#pc.setSize(None)
#pc.setProgram(prog_channel)
pc.setVisible(False)
enableLogScale(True)
enableColormapper(True)
elif dm =='Point':
for pci,pc in enumerate(parts):
pc.setProgram(prog_fixedColor)
pc.setData(None)
pc.setSize(None)
pc.setVisible(True)
enableColormapper(False)
enableLogScale(False)
elif dm =='Metal Surface Density':
for pci,pc in enumerate(parts):
if pc.is_gas_particles:
pc.setProgram(prog_channel)
pc.setSize(None)
pc.setVisible(True)
pc.setData(pc.gas_met_sd_dim)
#update colormap bounds to 5 sigma in either logspace or not
updateColormapBounds(*np_tools.setDefaultRanges(pc.gas_met_sd))
else:
#pc.setData(None)
#pc.setSize(None)
#pc.setProgram(prog_channel)
pc.setVisible(False)
enableLogScale(True)
enableColormapper(True)
elif dm =='SFR':
for pci,pc in enumerate(parts):
if pc.is_gas_particles:
pc.setProgram(prog_channel)
pc.setSize(None)
pc.setVisible(True)
pc.setData(pc.gas_sfr_dim)
#update colormap bounds to 5 sigma in either logspace or not
updateColormapBounds(*np_tools.setDefaultRanges(pc.gas_sfr))
else:
#pc.setData(None)
#pc.setSize(None)
#pc.setProgram(prog_channel)
pc.setVisible(False)
enableLogScale(True)
enableColormapper(True)
setDecimationValue(1)
elif dm =='Gas Velocities':
for pci,pc in enumerate(parts):
if pc.is_gas_particles:
pc.setVisible(True)
pc.setProgram(prog_vector)
pc.setVectorData(pc.vx_dim,pc.vy_dim,pc.vz_dim)
else:
pc.setVisible(False)
redraw()
def orientCamera(self,res,weight,radius):
"""
Initializes the camera pointing towards the center of mass of the given
particle dictionary, res. Additionally (included here for brevity)
sets the colormap bounds and point scale according to the density
of the gas particles (here weight/radius is assumed to be taken
from the gas particles but in principle could be anything).
Input:
res -
weight -
radius -
Modifies:
cameraPosition -
pivotPoint -
cameraOrientation -
colormapMin -
colormapMax -
PointScale -
"""
global cameraPosition,pivotPoint,cameraOrientation
#orient camera pivot and position on center of mass
if orientOnCoM:
cameraPosition,pivotPoint,cameraOrientation=np_tools.setCenterOfMassView(res['p'],res['m'],distanceFromCoM)
#2 sigma of the log of the array
#sets global variable PointScale -> 1
#but could in principle be some function of the radii
np_tools.setDefaultPointScale(radius)
#### Ideally this is moved inside another class as well...
# impossible until we create a class that can hold parts and
# has a loader attribute to keep track of the loader that was used.
print "loading base: " + str(datasetBase) + " number: " + str(snapshotNumber)
loader = Loader(datasetBase,snapshotNumber)
#read in snapshot data
parts=[]
colors = ['red','yellow','blue']
for i,ptype in enumerate([0,1,4][:3]):
res = loader.importParticleData(ptype)
## strictly this should be a list *only* of the keys that we care about
# that way we can loop over them to be a bit more general in the future.
keys=['p','v']
keys=res.keys()
pc = loader.getPointcloud(res,keys,colors[i],lowres=ptype==1)
#for easy iteration
parts+=[pc]
#hacky way to pass setDataMode wherever it needs to be...
global setDataMode
setDataMode=loader.setDataMode