Skip to content

adamdempsey90/disk_data_analysis

 
 

Repository files navigation

disk_data_analysis - Analysis and Plotting Tools for HDF5 simulation data of meshless/moving-mesh hydrodynamical disks ================================================== .. sectnum:

.. class:: no-web

image

Overview

A Python package!

Installation --------

You need to have git installed. In addition, you need the NumPy and SciPy Python packages.

git clone https://github.com/djmunoz/disk_data_analysis.git

cd disk_data_analysis

sudo python setup.py install

That is all!

Examples

Reading-in and plotting data ~~~~~~~

First of all, load the package and check the functions/attributes in it:

import disk_data_analysis.circumbinary as dda

dir(dda)

The most basic function in that list is get_snapshot_data, which can be used to give

snap = dda.get_snapshot_data('./data/snap_',0,['POS','VEL','R'])

which will only contain the requested quantities POS (positions) and VEL (velocities). You can double-check the attributes of the snap data structure by doing

dir(snap.gas)

You can see the distribution of particles/mesh generating points by plotting the positions

import matplotlib.pyplot as plt

x = snap.gas.POS[:,0]
y = snap.gas.POS[:,1]
box = snap.header.boxsize
plt.plot(x,y,'b.',ms=2.4)
plt.xlim(0.5 * box - 2.5, 0.5 * box + 2.5)
plt.ylim(0.5 * box - 2.5, 0.5 * box + 2.5)
plt.xlabel(r'$x$',size=18)
plt.ylabel(r'$y$',size=18)
plt.show()

image

Voronoi mesh '''''''

You can compare the mesh-generating scatter plot above with the associated Voronoi grid. Fortunately, Python has built-in Voronoi routines in 2-D.

from scipy.spatial import Voronoi, voronoi_plot_2d
ind = snap.gas.R < 3.8
points = np.array([snap.gas.POS[ind,0],snap.gas.POS[ind,1]]).T

vor = Voronoi(points)

fig = plt.figure(figsize=(4.5,4.5))
voronoi_plot_2d(vor,show_points=False,show_vertices=False)
plt.xlim(0.5 * snap.header.boxsize - 2.5, 0.5 * snap.header.boxsize + 2.5)
plt.ylim(0.5 * snap.header.boxsize - 2.5, 0.5 * snap.header.boxsize + 2.5)
plt.xlabel(r'$x$',size=18)
plt.ylabel(r'$y$',size=18)
plt.axes().set_aspect('equal')
plt.show()

image

Computing radial profiles ~~~~~~~

Mapping onto polar grids ~~~~~~~

Often, SPH and moving-mesh simulations of disks will be compared to simulation results obtained with polar-grid codes. In particular, comparison of azimuthally-averaged quantities is common practice. While azimuthal averaging is trivial in polar grids, it requires some tinkering when computational cells (or particles) are not placed in regular intervals in radius. One way around this is to remap the fluid's primitive variables into a structured grid of points.

import numpy as np

# We need the density and positions
snap = dda.get_snapshot_data('./data/snap_',0,['POS','RHO'])


# get a sense of the dynamical range in radius in the simulation
Rmin, Rmax = 1.0, 80.0
NR, Nphi = 200, 400
grid = dda.grid_polar(NR = NR, Nphi = Nphi,Rmin=1.0,Rmax= 80.0,scale='log')
grid.X, grid.Y = grid.X + snap.header.boxsize * 0.5, grid.Y  +  snap.header.boxsize * 0.5

rho_interp = dda.disk_interpolate_primitive_quantities(snap,[grid.X,grid.Y],quantities=['RHO'])[0]

# And now we can plot the density field of this structured grid
fig = plt.figure(figsize=(5,4.5))
fig.subplots_adjust(top=0.97,right=0.95,left=0.1,bottom=0.12)
ax = fig.add_subplot(111)
ax.scatter(grid.X,grid.Y,c=rho_interp ,lw=0)
ax.axis([76,84,76,84])
ax.set_xlabel(r'$x$',size=18)
ax.set_ylabel(r'$y$',size=18)
ax.set_aspect(1.0)
plt.show()

image

An interpolated, structured grid can be used to map AREPO snapshots into data readable by other codes like FARGO and PLUTO. But you might find other used for the polar regridding, such as computing radial profiles for diverse quantities. In such case, since AREPO (and SPH) simulation will in general have unevenly populated resolution elements, you might want to have a "nested" polar grid such as:

grid_in = dda.grid_polar(NR = 80, Nphi = 600, Rmin = 1.0, Rmax= 4.0, scale='linear')
grid_in.X, grid_in.Y = grid_in.X + snap.header.boxsize * 0.5, grid_in.Y  +  snap.header.boxsize * 0.5
grid_out = dda.grid_polar(NR = 140, Nphi = 300, Rmin = 4.0, Rmax= 80.0, scale='log')
grid_out.X, grid_out.Y = grid_out.X + snap.header.boxsize * 0.5, grid_out.Y  +  snap.header.boxsize * 0.5

X = np.append(grid_in.X,grid_out.X)
Y = np.append(grid_in.Y,grid_out.Y)

You can repeat the re-gridding step as before

rho_interp = dda.disk_interpolate_primitive_quantities(snap,[X,Y],quantities=['RHO'])[0]
fig = plt.figure(figsize=(5,4.5))
fig.subplots_adjust(top=0.97,right=0.95,left=0.1,bottom=0.12)
ax = fig.add_subplot(111)
ax.scatter(X,Y,c=rho_interp ,lw=0,s=8)
ax.axis([73,87,73,87])
ax.set_xlabel(r'$x$',size=18)
ax.set_ylabel(r'$y$',size=18)
ax.set_aspect(1.0)
plt.show()

and plot the color-coded cell locations as before

image

A uniform (and linear) sampling in R and phi allows us to create an reconstructed image plot in polar coordinates

Rmin, Rmax = 1.0, 7.0
NR, Nphi = 300, 500
R, phi = np.meshgrid(np.arange(Rmin,Rmax,(Rmax-Rmin)/NR),\
                 np.linspace(0,2*np.pi-np.pi/Nphi,Nphi))
X, Y = R * np.cos(phi) + snap.header.boxsize * 0.5, R * np.sin(phi) + snap.header.boxsize * 0.5
rho_interp = dda.disk_interpolate_primitive_quantities(snap,[X,Y],quantities=['RHO'])[0]


fig = plt.figure(figsize=(8,5))
fig.subplots_adjust(top=0.97,right=0.95,left=0.1,bottom=0.12)
ax = fig.add_subplot(111)
ax.imshow(rho_interp, origin='lower',interpolation='bilinear',
   extent=[R.min(),R.max(),phi.min()/np.pi,phi.max()/np.pi])
ax.set_xlabel(r'$R$',size=18)
ax.set_ylabel(r'$\phi/\pi$',size=18)
X0, X1 = ax.get_xlim()
Y0, Y1 = ax.get_ylim()
ax.set_aspect(float((X1-X0)/(Y1-Y0)/1.6))
plt.show()

image

Using re-gridded data to plot radial profiles '''''''' Simple quantities such as "first order" primitive variables RHO, VELPHI and VELR can be evaluated over a strucured grid and plotted as a function or R. A simple average can be used to compute the radial profile for all these quantities.

snap = dda.get_snapshot_data('./data/snap_',0,['POS','RHO','VELPHI','VELR'])

Rmin, Rmax = 1.0, 15.0
NR, Nphi = 300, 500
R, phi = np.meshgrid(np.arange(Rmin,Rmax,(Rmax-Rmin)/NR),\
np.linspace(0,2*np.pi-np.pi/Nphi,Nphi))
X, Y = R * np.cos(phi) + snap.header.boxsize * 0.5, R * np.sin(phi) + snap.header.boxsize * 0.5
rho_interp,velphi_interp,velr_interp  = dda.disk_interpolate_primitive_quantities(snap,[X,Y],quantities=['RHO','VELPHI','VELR'])

Rvals = R.mean(axis=0)
sigma0 = rho_interp.mean()
sigma_av = rho_interp.mean(axis=0)    
velphi_av = velphi_interp.mean(axis=0)
velr_av = velr_interp.mean(axis=0)

fig = plt.figure(figsize=(16,4))
ax = fig.add_subplot(1,3,1)
ax.plot(R,rho_interp/sigma0,'b.',ms=2.5)
ax.plot(Rvals,sigma_av/sigma0,color='darkred',lw=5.0,alpha=0.6) 

ax = fig.add_subplot(1,3,2)
ax.plot(R,velphi_interp,'b.',ms=2.5)
ax.plot(Rvals,velphi_av,color='darkred',lw=5.0,alpha=0.6) 

ax = fig.add_subplot(1,3,3)
ax.plot(R,velr_interp,'b.',ms=2.5)
ax.plot(Rvals,velr_av,color='darkred',lw=5.0,alpha=0.6) 

plt.show()

image

Computing other basic quantities, such as the mass accretion profile in disks, can be non-trivial. In conservative, finite-volume codes, one can in principle store the flux of any conserved quantity across a given radial location provided: (1) the cell boundaries align with the radial coordinate (i.e., the a cylindrical grid is used to discretize the domain) and (2) the quantity under question is directly evolved in a conservative manner (i.e., by used of a Godunov-like scheme). AREPO simulations fail to meet the first requirement, since the mesh is unstructured. In such case, interpolation is left as the most straightforward tool to compute the flux of conserved quantities crossing a boundary at a specific value of radius R.

For the mass accretion rate:

image

Displaying 2-D fields without pre-computed image data ~~~~~~~

About

Analysis and Plotting Tools for HDF5 simulation data of meshless/moving-mesh hydrodynamical disks

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Python 100.0%