Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

yt 4.0 adding arbitrary_grid to interpolate SPH data #1814

Merged
merged 45 commits into from
Jun 18, 2018
Merged

yt 4.0 adding arbitrary_grid to interpolate SPH data #1814

merged 45 commits into from
Jun 18, 2018

Conversation

AshKelly
Copy link
Member

@AshKelly AshKelly commented Jun 4, 2018

PR Summary

PR to add a new feature to interpolate SPH fields onto an arbitrary grid.

  • Add the cython pixelize function which takes data in 3D and a returns a 3D buffer
  • wire into the arbitrary grid container
  • Add test to compare the arbitrary_grid with the slice plot

An example use case of this new wiring is as follows,

import yt
from yt.testing import \
    assert_equal, \
    fake_sph_orientation_ds, fake_random_ds

ds = fake_sph_orientation_ds()

agrid = ds.arbitrary_grid([0.0, 0.0, 0.0], [2.99, 2.99, 2.99],dims=[5, 2, 2])
dens = agrid['gas', 'density']

print(dens)

PR Checklist

  • Code passes flake8 checker
  • New features are documented, with docstrings and narrative docs
  • Adds a test for any bugs fixed. Adds tests for new features.

…identifying any neighbouring pixels? will attempt to debug later
…se) always debug cython code with bounds checking on
…rbitary grid class. Appears to be almost working -- other than returning an array of zeros. will debug
buff[xi, yi, zi] += coeff * kernel_func(qxy)

if use_norm:
# now we can calculate the normalized image buffer we want to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd just say buffer since this is a 3D grid, not an image.

if(is_sph_field):
self._fill_sph_particles(fields)
else:
self._fill_particles(part)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's work through this together during our call tomorrow, I'm a little confused about your concern in the PR description and I think it might be easier to chat about it over a telecon.

pz = chunk[(ptype,'particle_position_z')].in_units('cm')
hsml = chunk[(ptype,'smoothing_length')].in_units('cm')*10.0
mass = chunk[(ptype,'particle_mass')].in_units('g')
dens = chunk[(ptype,'density')].in_units('g/cm**3')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could probably be doing this in code units. both here and in the image pixelizers. it doesn't really matter which unit system we choose as long as we stick to a single system, but code units is probably the most natural choice.

bounds[4] = self.left_edge[2]
bounds[1] = self.right_edge[0]
bounds[3] = self.right_edge[1]
bounds[5] = self.right_edge[2]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these always in 'cm'?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it even matter? it might not if the cython function ignores units.

for field in fields:
dest = np.zeros(self.ActiveDimensions, dtype="float64")

for chunk in self._data_source.chunks(fields, "io"):
Copy link
Member

@ngoldbaum ngoldbaum Jun 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think you should pass in [field] here?

bounds[5] = self.right_edge[2]

pixelize_sph_kernel_arbitrary_grid(dest,px,py,pz,hsml,mass,
dens,mass,bounds)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, did you forget to pass in the field values for the field we're smoothing onto the grid?

@@ -1154,6 +1154,130 @@ def pixelize_sph_kernel_slice(
continue
buff[xi, yi] += buff_num[xi, yi] / buff_denom[xi, yi]

@cython.initializedcheck(True)
@cython.boundscheck(True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these should be turned off when we're sure everything is correct

for j in prange(0, posx.shape[0]):
if j % 1000 == 0:
with gil:
PyErr_CheckSignals()
Copy link
Member

@ngoldbaum ngoldbaum Jun 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd like to see a progress bar here as well

w_j = pmass[j] / pdens[j] / hsml[j]**3
coeff = w_j * quantity_to_smooth[j]

# Now we know which pixels to deposit onto for this particle,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

voxels, not pixels

@ngoldbaum
Copy link
Member

Not at all :)

In general you don’t need to ask permission to use code I share publicly, just assume the answer is yes.

@qobilidop
Copy link
Member

@ngoldbaum Just tried your script on FIRE_M12i_ref11/snapshot_600.hdf5, and I see the VR of this snapshot for the first time! I can't wait to do some VR on the FIRE data I'm currently analyzing!
uniformgriddata_render_density
@AshKelly Thanks for your fantastic work! This is super useful.

@qobilidop
Copy link
Member

qobilidop commented Jun 13, 2018

I'm trying to compare a slice buffer from the arbitrary grid and a slice buffer made from the slice plot directly using the following script:

import yt
import numpy as np
from yt.units import kpc

ds = yt.load('GadgetDiskGalaxy/snapshot_200.hdf5')
_, c = ds.find_max(('gas', 'density'))
width = 50 * kpc
buff_size = 256
field = ('gas', 'density')

# buffer from arbitrary grid
ag = ds.arbitrary_grid(c - width / 2,
                       c + width / 2,
                       [buff_size]*2 + [1])
buff_ag = ag[field][:, :, 0].to('g*cm**-3').d

# buffer from slice
p = yt.SlicePlot(ds, 'z', field, center=c, width=width)
p.set_buff_size(buff_size)
buff_slc = p.frb.data[field].to('g*cm**-3').d

# comparison plot
from matplotlib.colors import LogNorm
import matplotlib.pyplot as plt

plt.figure(figsize=(10, 4))

plt.subplot(1, 2, 1)
plt.imshow(buff_ag, norm=LogNorm())
plt.colorbar()
plt.title('arbitrary grid')

plt.subplot(1, 2, 2)
plt.imshow(buff_slc, norm=LogNorm())
plt.colorbar()
plt.title('slice')

plt.tight_layout()
plt.savefig('ag_slc_cmp.png')

And here's the result:
ag_slc_cmp
It looks like a lot of particles are missed in the arbitrary grid.

@chummels
Copy link
Member

Holy cow--that VR on Gadget data is awesome!

@AshKelly
Copy link
Member Author

AshKelly commented Jun 13, 2018

I disagree @qobilidop. I have a suspicion the difference is due to normalization. In the slice plot we don't normalize because the results look better without it, but since the arbitrary grid may be used for analysis we went with normalization by default. I can just check this on my machine though.

update:
This doesn't seem to be normalization causing this.

I suspect the confusion is coming from how the arbitrary_grid works. You can select a huge region, but, just because you have selected every particle in that region does not mean their smoothing lengths will overlap with the center of the voxels, remember the smoothing length uses separation in x,y and z for the arbitrary_grid. In the slice plot, the smoothing length does not use contributions from the z-direction and so particles far away in the z direction will still be included, I believe. (Please correct if I'm wrong here @ngoldbaum)

There was certainly a bug, I'd missed a sqrt which could have been missing particles. I have now fixed this. There also seems to be a difference in the orientation of the return arrays from slice and arbitrary grid. Maybe slice has a transpose I missed or something. If I modify your script to this

import yt
import numpy as np
from yt.units import kpc

ds = yt.load('GadgetDiskGalaxy/snapshot_200.hdf5')
_, c = ds.find_max(('gas', 'density'))
width = 50 * kpc
buff_size = 256
field = ('gas', 'density')

# buffer from arbitrary grid
ag = ds.arbitrary_grid(c - width / 2,
                       c + width / 2,
                       [buff_size]*2 + [1])
buff_ag = ag[field][:, :, 0].to('g*cm**-3').d
buff_ag = buff_ag.T

# buffer from slice
p = yt.SlicePlot(ds, 'z', field, center=c, width=width)
p.set_buff_size(buff_size)
buff_slc = p.frb.data[field].to('g*cm**-3').d

# comparison plot
from matplotlib.colors import LogNorm
import matplotlib.pyplot as plt

plt.figure(figsize=(10, 4))

plt.subplot(1, 2, 1)
plt.imshow(buff_ag, norm=LogNorm())
plt.colorbar()
plt.title('arbitrary grid')

plt.subplot(1, 2, 2)
plt.imshow(buff_slc, norm=LogNorm())
plt.colorbar()
plt.title('slice')

plt.tight_layout()
plt.savefig('ag_slc_cmp.png')

then I get the following:
ag_slc_cmp

but I don't know why values are off.

I think the following line is causing the strange results in the grid,
h_j3 = fmax(hsml[j]*hsml[j]*hsml[j], dx*dy*dz)
as dz is huge, this will make the smoothing length huge so the contributions from most particles will be very small! Thoughts on the best way to deal with this? If we change this then particles far away won't be included in the contribution even if we are in the voxel - but in its current state the slice images look strange.

I also think we can avoid the sqrt I had to add by saying
2 * x < 1
is equivalent to
4 * x**2 < 1
just wanted to check that this logic is sound?

@dnarayanan
Copy link
Contributor

this is amazing stuff! well done!

@ngoldbaum
Copy link
Member

@qobilidop I suspect if you plot that on the same colorbar scale the differences will appear smaller.

@AshKelly
Copy link
Member Author

ag_slc_cmp

They definitely seem to be getting the same results, but the grid looks massively over smoothed due to the
h_j3 = fmax(hsml[j]*hsml[j]*hsml[j], dx*dy*dz)

@ngoldbaum
Copy link
Member

Do you understand why there are zones with no data in the arbitrary grid but not in the slice?

@AshKelly
Copy link
Member Author

no I don't, I'm going to look into that now

@AshKelly
Copy link
Member Author

It looks like we aren't passing in as many particles in the arbitrary_grid call as we are in the slice call. Any reason why this might be?

Every chunk passes more particles to pixelize_slice and than pixelize_arbitrary ...

@ngoldbaum
Copy link
Member

Hmm can you share the test script you used to check? Just the one Bili shared upthread?

@AshKelly
Copy link
Member Author

AshKelly commented Jun 13, 2018

I'm using this script (I think its the same as upthread)

import yt
import numpy as np
from yt.units import kpc

ds = yt.load('GadgetDiskGalaxy/snapshot_200.hdf5')
_, c = ds.find_max(('gas', 'density'))
width = 50 * kpc
buff_size = 256
field = ('gas', 'density')

# buffer from arbitrary grid
ag = ds.arbitrary_grid(c - width / 2,
                       c + width / 2,
                       [buff_size]*2 + [1])
buff_ag = ag[field][:, :, 0].to('g*cm**-3').d
buff_ag = buff_ag.T

# buffer from slice
p = yt.SlicePlot(ds, 'z', field, center=c, width=width)
p.set_buff_size(buff_size)
buff_slc = p.frb.data[field].to('g*cm**-3').d

# comparison plot
from matplotlib.colors import LogNorm
import matplotlib.pyplot as plt

plt.figure(figsize=(10, 4))

plt.subplot(1, 2, 1)
plt.imshow(buff_ag, norm=LogNorm(), vmin=10**(-29), vmax=10**(-22))
plt.colorbar()
plt.title('arbitrary grid')

plt.subplot(1, 2, 2)
plt.imshow(buff_slc, norm=LogNorm(), vmin=10**(-29), vmax=10**(-22))
plt.colorbar()
plt.title('slice')

plt.tight_layout()
plt.savefig('ag_slc_cmp.png')

with the extra prints I've just committed. You can see that a differing number of particles is passed. I presume something is going on with the region/slice code?

# yt's kernel functions use a different convention than
# the SPLASH paper (following Gadget-2), and qxy is
# twice the value of q used in the SPLASH paper
qxy = 2.0 * math.sqrt(posx_diff + posy_diff + posz_diff)
Copy link
Member

@ngoldbaum ngoldbaum Jun 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should just call this q here and in the other SPH pixelizers. It's called q in the paper, so it makes it a little easier to compare with the paper. In addition, here z is included as well, so qxy is a bit of a misnomer.

I'd also add in the comment above to see equation 5 in the SPLASH paper.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also should be one half the value in the SPLASH paper, and the factor of 2.0 should be deleted. See the patch I sent you over slack.

@AshKelly
Copy link
Member Author

Just need to check this still passes my test... will wait for the results

@ngoldbaum ngoldbaum merged commit 53cac3f into yt-project:yt-4.0 Jun 18, 2018
@ngoldbaum
Copy link
Member

Great job on seeing this one through Ash! 🎆 💯 🥇 🎉 :shipit:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants