PITP 2016: Computational Plasma Astrophysics

yt: A Python-based Framework for Visualization and Analysis of Physical Simulations

John ZuHone

Harvard-Smithsonian Center for Astrophysics

This presentaion is available at the following URL as slides and a Jupyter notebook at the following URLs:

http://yt-project.org/pitp2016_demo/yt_tutorial.slides.html

http://yt-project.org/pitp2016_demo/yt_tutorial.ipynb

The data I use in this presentation are available at the following URL:

http://yt-project.org/pitp2016_demo/data

The example notebooks I'll be working through in a focus session are available here:

http://yt-project.org/pitp2016_demo/examples

yt is a framework for working with the ouputs of simulation codes

visualization

  • slices
  • projections
  • volume rendering
  • phase diagrams

analysis

  • data selection and derived quantities
  • low-level data inspection
  • profiling (how does a quantity vary with radius? density?)
  • and much more...

yt supports many production astrophysical simulation codes

  • SPH (Gadget, Gasoline, Tipsy, ...)
  • Patch AMR (Enzo, Athena, Chombo, ...)
  • Octree AMR (FLASH, ART, ...)
  • Unstructured Mesh (Moab, Exodus II, ...)

yt runs on a variety of platforms

  • Operating systems: Linux, macOS, Windows (including the new Bash on Windows!)
  • Runs on everything from laptops (e.g., this one) to supercomputers (e.g., NASA's Pleiades)
  • Currently runs in Python 2.7 and 3.5
  • Source installs and binaries are available

Developed by a global team of astrophysics researchers to solve their real-world problems.

Tom Abel Andrew Cunningham Cameron Hummels Michael Kuhlen Desika Narayanan Hsi-Yu Schive Ting-Wai To
Gabriel Altay Bili Dong Suoqing Ji Meagan Lang Kaylea Nelson Anthony Scopatz Joseph Tomlinson
Kenza Arraki Nicholas Earl Allyson Julian Eve Lee Brian O'Shea Noel Scudder Stephanie Tonnesen
Kirk Barrow Hilary Egan Anni Järvenpää Doris Lee J.S. Oishi Sam Skillman Matthew Turk
Ricarda Beckmann Rasmi Elasmar Christian Karch Sam Leitner JC Passy Stephen Skory Casey W. Stark
Elliott Biondo Daniel Fenn Max Katz Stuart Levy John Regan Aaron Smith Miguel de Val-Borro
Alex Bogert John Forbes BW Keller Yuan Li Mark Richardson Britton Smith Rick Wagner
Robert Bradshaw Sam Geen Ji-hoon Kim Joshua Moloney Sherwood Richers Geoffrey So Mike Warren
Yi-Hao Chen Adam Ginsburg Steffen Klemer Christopher Moody Thomas Robitaille Antoine Strugarek Andrew Wetzel
Pengfei Chen Nathan Goldbaum Fabian Koller Stuart Mumford Anna Rosen Elizabeth Tasker John Wise
David Collins Eric Hallman Kacper Kowalik Andrew Myers Chuck Rozhon Benjamin Thompson Michael Zingale
Brian Crosby David Hannasch Mark Krumholz Jill Naiman Douglas Rudd Robert Thompson John ZuHone
In [1]:
from __future__ import print_function
import warnings
warnings.filterwarnings('ignore')
from IPython.display import IFrame

IFrame('http://yt-project.org/docs/dev/reference/code_support.html', width=960, height=600)
Out[1]:

Sample datasets loadable by yt

In [2]:
IFrame('http://yt-project.org/data', width=700, height=500)
Out[2]:

What is yt?

Getting started with the Basics

(slides adapted from Britton Smith's PyAstro 16 talk and a recent talk by Nathan Goldbaum at McMaster University)

Eulerian, gridded, AMR/SMR data vs. Lagrangian, particle-based, SPH data

Data on disk has no inherent physical meaning

yt lets you think about the data using a physically motivated interface

yt lets you think about the data using a physically motivated interface

yt's most fundamental concepts:

  • Data Objects
  • Fields
  • Units
  • Derived Quantities

yt lets you think about the data using a physically motivated interface

In [3]:
import yt
ds = yt.load('data/DD0046/DD0046')
yt : [INFO     ] 2016-07-18 15:08:59,770 Parameters: current_time              = 229.70846991283
yt : [INFO     ] 2016-07-18 15:08:59,771 Parameters: domain_dimensions         = [32 32 32]
yt : [INFO     ] 2016-07-18 15:08:59,773 Parameters: domain_left_edge          = [ 0.  0.  0.]
yt : [INFO     ] 2016-07-18 15:08:59,775 Parameters: domain_right_edge         = [ 1.  1.  1.]
yt : [INFO     ] 2016-07-18 15:08:59,777 Parameters: cosmological_simulation   = 1
yt : [INFO     ] 2016-07-18 15:08:59,778 Parameters: current_redshift          = 2.2204460492503e-16
yt : [INFO     ] 2016-07-18 15:08:59,780 Parameters: omega_lambda              = 0.732
yt : [INFO     ] 2016-07-18 15:08:59,781 Parameters: omega_matter              = 0.268
yt : [INFO     ] 2016-07-18 15:08:59,782 Parameters: hubble_constant           = 0.704

Allowing you to forget about what your data looks like as a file format

And select only the data you want to select

Data Objects

Physical objects to select data are called "data objects"

In [4]:
sp = ds.sphere("max", (2.0, "Mpc"))
Parsing Hierarchy : 100%|██████████| 124/124 [00:00<00:00, 14830.58it/s]
yt : [INFO     ] 2016-07-18 15:08:59,844 Gathering a field list (this may take a moment.)
yt : [INFO     ] 2016-07-18 15:09:01,917 Max Value is 1.12632e-26 at 0.4624023437500000 0.2124023437500000 0.5756835937500000

yt natively deals with multiresolution data

Data from each selected cell is returned as a flat, 1D array, with unit metadata attached

In [5]:
print(sp['density'])
[  3.77609805e-31   3.85824291e-31   3.69230079e-31 ...,   1.24169320e-29
   2.52070647e-30   1.55137848e-29] g/cm**3

Data objects can be queried for many fields

In [6]:
print(sp['temperature'])
[ 1676099.96624084  3020864.07440875  3031480.1098988  ...,
   343977.59450144   865379.75614703   750720.84717605] K

Spatial information is not lost

In [7]:
sp['x']
Out[7]:
YTArray([ 0.42578125,  0.42578125,  0.42578125, ...,  0.4609375 ,
        0.4609375 ,  0.4765625 ]) code_length
In [8]:
sp['x'].in_units('kpc')
Out[8]:
YTArray([ 19353.69318182,  19353.69318182,  19353.69318182, ...,
        20951.70454545,  20951.70454545,  21661.93181818]) kpc

Spatial information is not lost

In [9]:
import numpy as np

np.unique(sp['dx']).in_units('kpc')
Out[9]:
YTArray([  44.38920455,   88.77840909,  177.55681818,  355.11363636,
        710.22727273]) kpc
In [10]:
IFrame("http://yt-project.org/doc/analyzing/objects.html#available-objects", width=700, height=600)
Out[10]:
In [11]:
import yt.units as u

ad = ds.all_data()

box = ds.box(ds.domain_left_edge + 500*u.kpc, ds.domain_right_edge - 500*u.kpc)

sp = ds.sphere(ds.domain_center, 500*u.kpc)

disk = ds.disk(ds.domain_center, [0, 0, 1], 500*u.kpc, 100*u.kpc)

ray = ds.ray(ds.domain_left_edge, ds.domain_right_edge)

Fields

Data objects can be queried for many fields

In [12]:
from pprint import pprint

# fields that are defined in the on-disk data file
pprint(ds.field_list)
[('all', 'creation_time'),
 ('all', 'dynamical_time'),
 ('all', 'metallicity_fraction'),
 ('all', 'particle_index'),
 ('all', 'particle_mass'),
 ('all', 'particle_position_x'),
 ('all', 'particle_position_y'),
 ('all', 'particle_position_z'),
 ('all', 'particle_type'),
 ('all', 'particle_velocity_x'),
 ('all', 'particle_velocity_y'),
 ('all', 'particle_velocity_z'),
 ('enzo', 'Dark_Matter_Density'),
 ('enzo', 'Density'),
 ('enzo', 'Electron_Density'),
 ('enzo', 'GasEnergy'),
 ('enzo', 'HII_Density'),
 ('enzo', 'HI_Density'),
 ('enzo', 'HeIII_Density'),
 ('enzo', 'HeII_Density'),
 ('enzo', 'HeI_Density'),
 ('enzo', 'Metal_Density'),
 ('enzo', 'Temperature'),
 ('enzo', 'TotalEnergy'),
 ('enzo', 'x-velocity'),
 ('enzo', 'y-velocity'),
 ('enzo', 'z-velocity'),
 ('io', 'creation_time'),
 ('io', 'dynamical_time'),
 ('io', 'metallicity_fraction'),
 ('io', 'particle_index'),
 ('io', 'particle_mass'),
 ('io', 'particle_position_x'),
 ('io', 'particle_position_y'),
 ('io', 'particle_position_z'),
 ('io', 'particle_type'),
 ('io', 'particle_velocity_x'),
 ('io', 'particle_velocity_y'),
 ('io', 'particle_velocity_z')]

Data objects can be queried for many fields

In [13]:
# fields that yt can calculate given the fields in ds.field_list
# (only showing gas fields to fit on one screen)
pprint([f for f in ds.derived_field_list if f[0]=='gas'][:20])
[('gas', 'El_density'),
 ('gas', 'El_fraction'),
 ('gas', 'El_mass'),
 ('gas', 'El_number_density'),
 ('gas', 'H_density'),
 ('gas', 'H_fraction'),
 ('gas', 'H_mass'),
 ('gas', 'H_nuclei_density'),
 ('gas', 'H_number_density'),
 ('gas', 'H_p0_density'),
 ('gas', 'H_p0_fraction'),
 ('gas', 'H_p0_mass'),
 ('gas', 'H_p0_number_density'),
 ('gas', 'H_p1_density'),
 ('gas', 'H_p1_fraction'),
 ('gas', 'H_p1_mass'),
 ('gas', 'H_p1_number_density'),
 ('gas', 'He_density'),
 ('gas', 'He_fraction'),
 ('gas', 'He_mass')]

Particle fields

In [14]:
pprint([f for f in ds.field_list if f[0]=='io'])
[('io', 'creation_time'),
 ('io', 'dynamical_time'),
 ('io', 'metallicity_fraction'),
 ('io', 'particle_index'),
 ('io', 'particle_mass'),
 ('io', 'particle_position_x'),
 ('io', 'particle_position_y'),
 ('io', 'particle_position_z'),
 ('io', 'particle_type'),
 ('io', 'particle_velocity_x'),
 ('io', 'particle_velocity_y'),
 ('io', 'particle_velocity_z')]
In [15]:
ad = ds.all_data()

print(ad['io', 'particle_position'])
print(ad['io', 'particle_mass'])
[[ 0.99917849  0.99302715  0.04505799]
 [ 0.97151599  0.99000909  0.04001278]
 [ 0.92810786  0.96941202  0.03330838]
 ..., 
 [ 0.61380668  0.84492898  0.11017318]
 [ 0.61006628  0.84046116  0.11218501]
 [ 0.60696932  0.84205101  0.11330456]] code_length
[  2.54958423e-05   2.54958423e-05   2.54958423e-05 ...,   2.54958423e-05
   2.54958423e-05   2.54958423e-05] code_mass

Deposit Fields

In [16]:
pprint([f for f in ds.derived_field_list if f[0]=='deposit'])
[('deposit', 'all_cic'),
 ('deposit', 'all_cic_velocity_x'),
 ('deposit', 'all_cic_velocity_y'),
 ('deposit', 'all_cic_velocity_z'),
 ('deposit', 'all_count'),
 ('deposit', 'all_density'),
 ('deposit', 'all_mass'),
 ('deposit', 'all_nn_velocity_x'),
 ('deposit', 'all_nn_velocity_y'),
 ('deposit', 'all_nn_velocity_z'),
 ('deposit', 'io_cic'),
 ('deposit', 'io_cic_velocity_x'),
 ('deposit', 'io_cic_velocity_y'),
 ('deposit', 'io_cic_velocity_z'),
 ('deposit', 'io_count'),
 ('deposit', 'io_density'),
 ('deposit', 'io_mass'),
 ('deposit', 'io_nn_velocity_x'),
 ('deposit', 'io_nn_velocity_y'),
 ('deposit', 'io_nn_velocity_z')]

Derived Fields: create new fields by defining a python function

In [17]:
from yt.units import kboltz, mh

def my_entropy(field, data):
    return (kboltz * data['temperature'] / data['number_density']**(2./3.))

ds.add_field('entropy', function=my_entropy, units="keV*cm**2")

Derived Fields: create new fields by defining a python function

In [18]:
sp['entropy']
Out[18]:
YTArray([   9.8712904 ,   10.05900208,  116.28453857,  103.34896303,
          9.05717995,    9.63822214,  109.85636122,   96.60393576]) cm**2*keV

yt employs a field "democracy"

  • Fields on-disk and derived fields are treated in exactly the same way
  • Accessed the same way, visualized the same way
  • On-disk fields are mapped to a set of common fields, to faciliate using the same scripts across different datasets
In [19]:
print(sp["gas","density"])
[  9.72796224e-32   7.51133575e-32   1.11774404e-30   7.88742157e-31
   1.11512078e-31   9.97278399e-32   1.03069070e-30   9.20236791e-31] g/cm**3
In [20]:
print(sp["enzo","Density"]) # These are the same field, just aliased
[ 0.03898174  0.03009931  0.44790066  0.31606353  0.04468495  0.03996279
  0.41301678  0.36875586] code_mass/code_length**3
In [21]:
print(sp["enzo","Density"].in_cgs())
[  9.72796224e-32   7.51133575e-32   1.11774404e-30   7.88742157e-31
   1.11512078e-31   9.97278399e-32   1.03069070e-30   9.20236791e-31] g/cm**3

Units

We've already seen how data objects return fields as unit-aware NumPy arrays

In [22]:
pressure = sp['density']*sp['temperature']*kboltz/(0.6*mh)

pressure.in_units('Pa')
Out[22]:
YTArray([  2.29874917e-21,   1.52229231e-21,   1.58431101e-18,
         7.87551706e-19,   2.64815892e-21,   2.33940537e-21,
         1.30753605e-18,   9.51864956e-19]) Pa
In [23]:
pressure.in_units('dyne/cm**2')
Out[23]:
YTArray([  2.29874917e-20,   1.52229231e-20,   1.58431101e-17,
         7.87551706e-18,   2.64815892e-20,   2.33940537e-20,
         1.30753605e-17,   9.51864956e-18]) dyne/cm**2

yt has very flexible handling for units and unit conversions

In [24]:
rho = sp['density']

print (rho.in_units('g/cm**3'))
print ()
print (rho.in_units('Msun/kpc**3'))
print ()
print (rho.in_units('code_mass/code_length**3')) # What's this? 
[  9.72796224e-32   7.51133575e-32   1.11774404e-30   7.88742157e-31
   1.11512078e-31   9.97278399e-32   1.03069070e-30   9.20236791e-31] g/cm**3

[  1.43736244   1.10984311  16.51531184  11.6541196    1.64765518
   1.47353627  15.22904869  13.59702854] Msun/kpc**3

[ 0.03898174  0.03009931  0.44790066  0.31606353  0.04468495  0.03996279
  0.41301678  0.36875586] code_mass/code_length**3

Shorthand conversions for different unit systems

In [25]:
# Methods for MKSA and cgs/Gaussian units
print(rho.in_mks())
print()
print(rho.in_cgs())
[  9.72796224e-29   7.51133575e-29   1.11774404e-27   7.88742157e-28
   1.11512078e-28   9.97278399e-29   1.03069070e-27   9.20236791e-28] kg/m**3

[  9.72796224e-32   7.51133575e-32   1.11774404e-30   7.88742157e-31
   1.11512078e-31   9.97278399e-32   1.03069070e-30   9.20236791e-31] g/cm**3

Other unit systems are available

In [26]:
print(list(yt.unit_system_registry.keys()))
['cgs-ampere', 'cgs', 'imperial', 'DD0046', 'mks', 'geometrized', 'planck', 'galactic', 'solar']
In [27]:
yt.unit_system_registry['mks']
Out[27]:
mks Unit System
 Base Units:
  mass: kg
  time: s
  length: m
  temperature: K
  current_mks: A
  angle: rad
 Other Units:
  energy: J
  specific_energy: J/kg
  pressure: Pa
  force: N
  magnetic_field_mks: T
  charge_mks: C

Let's look at some of the unit systems

In [28]:
yt.unit_system_registry['imperial']
Out[28]:
imperial Unit System
 Base Units:
  length: ft
  mass: lbm
  temperature: R
  time: s
  angle: rad
 Other Units:
  force: lbf
  energy: ft*lbf
  pressure: lbf/ft**2
In [29]:
yt.unit_system_registry['geometrized']
Out[29]:
geometrized Unit System
 Base Units:
  length: l_geom
  mass: m_geom
  temperature: K
  time: t_geom
  angle: rad
 Other Units:

We can convert into these other systems, too

In [30]:
print(rho.in_base('imperial'))
print()
print(rho.in_base('galactic'))
print()
print(rho.in_base('geometrized'))
[  6.07296843e-30   4.68917372e-30   6.97784806e-29   4.92395643e-29
   6.96147163e-30   6.22580566e-30   6.43439185e-29   5.74485061e-29] lbm/ft**3

[  1.43736244   1.10984311  16.51531184  11.6541196    1.64765518
   1.47353627  15.22904869  13.59702854] Msun/kpc**3

[  1.57485099e-49   1.21600334e-49   1.80950569e-48   1.27688753e-48
   1.80525893e-49   1.61448496e-49   1.66857583e-48   1.48976300e-48] m_geom/l_geom**3

But don't do something silly!

In [31]:
T = sp['temperature']
In [32]:
T.convert_to_units('kpc') # oops
---------------------------------------------------------------------------
YTUnitConversionError                     Traceback (most recent call last)
<ipython-input-32-919b53ddef1e> in <module>()
----> 1 T.convert_to_units('kpc') # oops

/Users/jzuhone/Source/yt/yt/units/yt_array.py in convert_to_units(self, units)
    467 
    468         """
--> 469         new_units = _unit_repr_check_same(self.units, units)
    470         (conversion_factor, offset) = self.units.get_conversion_factor(new_units)
    471 

/Users/jzuhone/Source/yt/yt/units/yt_array.py in _unit_repr_check_same(my_units, other_units)
    197     if not my_units.same_dimensions_as(other_units):
    198         raise YTUnitConversionError(
--> 199             my_units, my_units.dimensions, other_units, other_units.dimensions)
    200 
    201     return other_units

YTUnitConversionError: Unit dimensionalities do not match. Tried to convert between K (dim (temperature)) and kpc (dim (length)).

You can also make your own unitful arrays, and quantities too

In [33]:
from yt import YTArray, YTQuantity
a = YTArray(np.random.random(10), "kpc")
print(a)
[ 0.81955693  0.98290329  0.07128665  0.04860733  0.84772189  0.24895699
  0.98488209  0.85199743  0.4395122   0.01294123] kpc
In [34]:
b = YTQuantity(1.0e-27, "erg/s/cm**2/steradian")
print(b)
1e-27 erg/(cm**2*s*steradian)

Other ways of using units

In [35]:
import yt.units as u
print(u.kpc)
1.0 kpc
In [36]:
x = np.array([4.,5.,6.])*u.kg*u.cm/u.s
print(x)
[ 4.  5.  6.] cm*kg/s

Physical Constants

In [37]:
from yt.units import G, kboltz, hbar
print(G)
print()
print(kboltz)
print()
print(hbar)
6.67384e-08 cm**3/(g*s**2)

1.3806488e-16 erg/K

1.0545717253362894e-27 erg*s
In [38]:
gal_system = yt.unit_system_registry['galactic']
print(gal_system.constants.G)
print()
print(gal_system.constants.kboltz)
print()
print(gal_system.constants.hbar)
4.498205661165364e-12 kpc**3/(Msun*Myr**2)

7.262444811589367e-66 Msun*kpc**2/(K*Myr**2)

1.7578093950982924e-90 Msun*kpc**2/Myr

Every dataset has a set of "code units"

In [39]:
for unit in ["length", "mass", "time", "velocity", "magnetic"]:
    print("code_%s =" % unit, getattr(ds, unit+"_unit"))
code_length = 32.0 Mpccm/h
code_mass = 6.885639111117815e+48 g
code_time = 1898476477341437.0 s
code_velocity = 1449234126.012771 cm/s
code_magnetic = 0.0004137213258267729 gauss

You can create arrays and quantities with code units too, provided you use the dataset

In [40]:
v = ds.arr([1.0, 2.0, 3.0], "code_length/code_time")
print(v.in_units("km/s"))
[  738792.78219099  1477585.56438199  2216378.34657298] km/s
In [41]:
p = ds.quan(3.0e-3, "code_mass/code_time**2/code_length")
print(p.in_units("keV/cm**3"))
0.02550450106560087 keV/cm**3

Basic arithmetic and most NumPy functions work on unitful arrays

In [42]:
rho + 200*u.Msun/u.kpc**3
Out[42]:
YTArray([  1.36331310e-29,   1.36109648e-29,   1.46535955e-29,
         1.43245936e-29,   1.36473635e-29,   1.36355793e-29,
         1.45665421e-29,   1.44560882e-29]) g/cm**3
In [43]:
np.sqrt(rho)
Out[43]:
YTArray([  3.11896814e-16,   2.74068162e-16,   1.05723414e-15,
         8.88111568e-16,   3.33934242e-16,   3.15797150e-16,
         1.01522938e-15,   9.59289732e-16]) sqrt(g)/cm**(3/2)
In [44]:
rho*sp["velocity_x"]
Out[44]:
YTArray([  2.14988539e-24,   1.69476910e-24,   9.70718833e-24,
         9.09023779e-24,   2.43511879e-24,   2.22425382e-24,
         9.85462002e-24,   1.05188104e-23]) g/(cm**2*s)

But again, don't do something silly!

In [45]:
rho + sp["velocity_x"]
---------------------------------------------------------------------------
YTUnitOperationError                      Traceback (most recent call last)
<ipython-input-45-b0098d433e74> in <module>()
----> 1 rho + sp["velocity_x"]

/Users/jzuhone/Source/yt/yt/units/yt_array.py in __add__(self, right_object)
    903 
    904         """
--> 905         ro = sanitize_units_add(self, right_object, "addition")
    906         return super(YTArray, self).__add__(ro)
    907 

/Users/jzuhone/Source/yt/yt/units/yt_array.py in sanitize_units_add(this_object, other_object, op_string)
    155             elif not np.any(this_object):
    156                 return ret
--> 157             raise YTUnitOperationError(op_string, inp.units, ret.units)
    158         ret = ret.in_units(inp.units)
    159     else:

YTUnitOperationError: The addition operator for YTArrays with units (g/cm**3) and (cm/s) is not well defined.

Derived Quantities

Derived quantities turn fields into single values

In [46]:
sp['cell_mass']
Out[46]:
YTArray([  8.19135097e+42,   6.32485878e+42,   9.41187216e+43,
         6.64153876e+43,   9.38978327e+42,   8.39750112e+42,
         8.67884668e+43,   7.74877856e+43]) g

Derived quantities turn fields into single values

\begin{equation} M = \sum_i m_i \end{equation}
In [47]:
sp.quantities.total_quantity('cell_mass')
Out[47]:
3.571138557332189e+44 g
In [48]:
sp.quantities.angular_momentum_vector()
Out[48]:
YTArray([ -9.93000428e+29,  -7.23255885e+30,  -9.17649110e+30]) cm**2/s
In [49]:
IFrame("http://yt-project.org/docs/dev/analyzing/objects.html#available-derived-quantities", width=700, height=600)
Out[49]:

NumPy-like operations

  • yt has another way to create rectangular-shaped regions using NumPy-like syntax
  • The resulting regions have useful NumPy-like operations
In [50]:
dd = ds.r[:,:,:] # A region containing all the data
In [51]:
print(dd.mean("density"))
print(dd.mean("temperature", weight="density"))
3.2359310861810075e-29 g/cm**3
4813733.901297271 K
In [52]:
dd.argmax("temperature")
Out[52]:
(0.8046875 code_length, 0.8671875 code_length, 0.9609375 code_length)
In [53]:
dd.argmax("temperature", axis=["density", "radius"])
Out[53]:
[9.122286925255487e-32 g/cm**3, 9.304984252044084e+25 cm]

NumPy-like operations

In [54]:
reg = ds.r[0.4:0.6,0.1:0.3,0.35:0.75] # subset of the domain in code units
In [55]:
reg.mean("density")
Out[55]:
2.717166127977496e-28 g/cm**3
In [56]:
c = ds.domain_center
w = 5.0*u.Mpc
le = c-0.5*w
re = c+0.5*w
In [57]:
reg2 = ds.r[le[0]:re[0],le[1]:re[1],le[2]:re[2]] # subset of the domain using a left and right edge
In [58]:
reg2.mean("density")
Out[58]:
5.233135741599468e-31 g/cm**3

Visualization and analysis

  • SlicePlot, ProjectionPlot, ParticleProjectionPlot
  • ProfilePlot, PhasePlot
  • Slices, projections, profiles, covering grids, and fixed resolution buffers

SlicePlot

In [59]:
from yt import SlicePlot
slc = SlicePlot(ds, 'x', ('gas', 'density'), center='max')
yt : [INFO     ] 2016-07-18 15:09:40,770 Max Value is 1.12632e-26 at 0.4624023437500000 0.2124023437500000 0.5756835937500000
yt : [INFO     ] 2016-07-18 15:09:40,813 xlim = -0.287598 0.712402
yt : [INFO     ] 2016-07-18 15:09:40,814 ylim = 0.075684 1.075684
yt : [INFO     ] 2016-07-18 15:09:40,815 xlim = -0.287598 0.712402
yt : [INFO     ] 2016-07-18 15:09:40,816 ylim = 0.075684 1.075684
yt : [INFO     ] 2016-07-18 15:09:40,824 Making a fixed resolution buffer of (('gas', 'density')) 800 by 800
In [60]:
slc.show()

In [61]:
slc.set_origin('native')
Out[61]:

In [62]:
ds2 = yt.load('data/virgo_novisc.0054.gdf')
slc2 = SlicePlot(ds2, 'z', ['magnetic_field_strength'], 
                 width=(500, 'kpc'), center='c')
yt : [INFO     ] 2016-07-18 15:09:43,360 Parameters: current_time              = 2700.122
yt : [INFO     ] 2016-07-18 15:09:43,361 Parameters: domain_dimensions         = [256 256 256]
yt : [INFO     ] 2016-07-18 15:09:43,362 Parameters: domain_left_edge          = [-2. -2. -2.]
yt : [INFO     ] 2016-07-18 15:09:43,363 Parameters: domain_right_edge         = [ 2.  2.  2.]
yt : [INFO     ] 2016-07-18 15:09:43,364 Parameters: cosmological_simulation   = 0.0
yt : [INFO     ] 2016-07-18 15:09:45,092 xlim = -0.250000 0.250000
yt : [INFO     ] 2016-07-18 15:09:45,092 ylim = -0.250000 0.250000
yt : [INFO     ] 2016-07-18 15:09:45,094 xlim = -0.250000 0.250000
yt : [INFO     ] 2016-07-18 15:09:45,094 ylim = -0.250000 0.250000
yt : [INFO     ] 2016-07-18 15:09:45,096 Making a fixed resolution buffer of (('gas', 'magnetic_field_strength')) 800 by 800
In [63]:
slc2.show()

In [64]:
slc.set_cmap('density', 'magma')
Out[64]:

Plot callbacks

In [65]:
slc.annotate_grids()
Out[65]:

In [66]:
slc2.annotate_magnetic_field()
Out[66]:

In [67]:
slc2.annotate_clear()
slc2.annotate_streamlines('velocity_x', 'velocity_y')
Out[67]:

Line integral convolution (talk to Suoqing Ji, who is here!)

In [68]:
slc2.annotate_clear()
slc2.annotate_line_integral_convolution('magnetic_field_x', 'magnetic_field_y')
Out[68]:

In [69]:
slc.save('my_amazing_plot.eps')
yt : [INFO     ] 2016-07-18 15:09:54,888 Saving plot my_amazing_plot.eps
Out[69]:
['my_amazing_plot.eps']
In [70]:
slc.save('my_amazing_plot.png')
yt : [INFO     ] 2016-07-18 15:09:55,491 Saving plot my_amazing_plot.png
Out[70]:
['my_amazing_plot.png']
In [71]:
from yt.units import mp
import numpy as np

def _sound_speed(field, data):
    return np.sqrt(5./3.*data['kT']/(0.6*mp))

def _mach_number(field, data):
    return data["velocity_magnitude"]/data["speed_of_sound"]

ds2.add_field(('gas', 'speed_of_sound'), _sound_speed, units='cm/s', force_override=True)
ds2.add_field(('gas', 'mach'), _mach_number, units='', force_override=True)
In [72]:
slc = yt.SlicePlot(ds2, 'z', ["speed_of_sound"], width=(200., 'kpc'))
slc.set_unit('speed_of_sound', 'km/s')
slc.set_log('speed_of_sound', False)
slc.set_zlim('speed_of_sound', 600., 1000.)
yt : [INFO     ] 2016-07-18 15:09:56,268 xlim = -0.100000 0.100000
yt : [INFO     ] 2016-07-18 15:09:56,269 ylim = -0.100000 0.100000
yt : [INFO     ] 2016-07-18 15:09:56,271 xlim = -0.100000 0.100000
yt : [INFO     ] 2016-07-18 15:09:56,273 ylim = -0.100000 0.100000
yt : [INFO     ] 2016-07-18 15:09:56,275 Making a fixed resolution buffer of (('gas', 'speed_of_sound')) 800 by 800
Out[72]:

In [73]:
slc = yt.SlicePlot(ds2, 'z', ["mach"], width=(200., 'kpc'))
slc.set_log('mach', False)
slc.set_zlim('mach', 0.1, 0.8)
yt : [INFO     ] 2016-07-18 15:09:58,165 xlim = -0.100000 0.100000
yt : [INFO     ] 2016-07-18 15:09:58,165 ylim = -0.100000 0.100000
yt : [INFO     ] 2016-07-18 15:09:58,167 xlim = -0.100000 0.100000
yt : [INFO     ] 2016-07-18 15:09:58,168 ylim = -0.100000 0.100000
yt : [INFO     ] 2016-07-18 15:09:58,169 Making a fixed resolution buffer of (('gas', 'mach')) 800 by 800
Out[73]:

ProjectionPlot

In [74]:
from yt import ProjectionPlot

prj = ProjectionPlot(ds2, "z", 'density')
yt : [INFO     ] 2016-07-18 15:10:21,566 Projection completed
yt : [INFO     ] 2016-07-18 15:10:21,578 xlim = -2.000000 2.000000
yt : [INFO     ] 2016-07-18 15:10:21,579 ylim = -2.000000 2.000000
yt : [INFO     ] 2016-07-18 15:10:21,582 xlim = -2.000000 2.000000
yt : [INFO     ] 2016-07-18 15:10:21,583 ylim = -2.000000 2.000000
yt : [INFO     ] 2016-07-18 15:10:21,594 Making a fixed resolution buffer of (('gas', 'density')) 800 by 800
In [75]:
prj
Out[75]:

In [76]:
prj.zoom(20)
yt : [INFO     ] 2016-07-18 15:10:22,717 Making a fixed resolution buffer of (('gas', 'density')) 800 by 800
Out[76]:

In [77]:
prj.zoom(3)
yt : [INFO     ] 2016-07-18 15:10:23,386 Making a fixed resolution buffer of (('gas', 'density')) 800 by 800
Out[77]:

In [78]:
ProjectionPlot(ds2, "z", 'density', width=(300.,"kpc"))
yt : [INFO     ] 2016-07-18 15:10:42,553 Projection completed
yt : [INFO     ] 2016-07-18 15:10:42,562 xlim = -0.150000 0.150000
yt : [INFO     ] 2016-07-18 15:10:42,562 ylim = -0.150000 0.150000
yt : [INFO     ] 2016-07-18 15:10:42,566 xlim = -0.150000 0.150000
yt : [INFO     ] 2016-07-18 15:10:42,567 ylim = -0.150000 0.150000
yt : [INFO     ] 2016-07-18 15:10:42,572 Making a fixed resolution buffer of (('gas', 'density')) 800 by 800
Out[78]:

In [79]:
ProjectionPlot(ds2, "x", 'density', width=(300.,"kpc"))
yt : [INFO     ] 2016-07-18 15:11:07,492 Projection completed
yt : [INFO     ] 2016-07-18 15:11:07,501 xlim = -0.150000 0.150000
yt : [INFO     ] 2016-07-18 15:11:07,501 ylim = -0.150000 0.150000
yt : [INFO     ] 2016-07-18 15:11:07,504 xlim = -0.150000 0.150000
yt : [INFO     ] 2016-07-18 15:11:07,505 ylim = -0.150000 0.150000
yt : [INFO     ] 2016-07-18 15:11:07,509 Making a fixed resolution buffer of (('gas', 'density')) 800 by 800
Out[79]:

In [80]:
ProjectionPlot(ds2, "y", 'density', width=(300.,"kpc"))
yt : [INFO     ] 2016-07-18 15:11:27,796 Projection completed
yt : [INFO     ] 2016-07-18 15:11:27,803 xlim = -0.150000 0.150000
yt : [INFO     ] 2016-07-18 15:11:27,804 ylim = -0.150000 0.150000
yt : [INFO     ] 2016-07-18 15:11:27,807 xlim = -0.150000 0.150000
yt : [INFO     ] 2016-07-18 15:11:27,808 ylim = -0.150000 0.150000
yt : [INFO     ] 2016-07-18 15:11:27,811 Making a fixed resolution buffer of (('gas', 'density')) 800 by 800
Out[80]:

In [81]:
sp = ds.sphere("max", (5.0, 'Mpc'))

prj = ProjectionPlot(ds, "z", 'density', data_source=sp)
yt : [INFO     ] 2016-07-18 15:11:29,133 Max Value is 1.12632e-26 at 0.4624023437500000 0.2124023437500000 0.5756835937500000
yt : [INFO     ] 2016-07-18 15:11:29,153 Projection completed
yt : [INFO     ] 2016-07-18 15:11:29,155 xlim = 0.000000 1.000000
yt : [INFO     ] 2016-07-18 15:11:29,156 ylim = 0.000000 1.000000
yt : [INFO     ] 2016-07-18 15:11:29,158 xlim = 0.000000 1.000000
yt : [INFO     ] 2016-07-18 15:11:29,159 ylim = 0.000000 1.000000
yt : [INFO     ] 2016-07-18 15:11:29,161 Making a fixed resolution buffer of (('gas', 'density')) 800 by 800
In [82]:
prj
Out[82]:

In [83]:
ProjectionPlot(ds2, 2, 'kT', weight_field='density', width=(500, 'kpc'))
yt : [INFO     ] 2016-07-18 15:11:56,385 Projection completed
yt : [INFO     ] 2016-07-18 15:11:56,392 xlim = -0.250000 0.250000
yt : [INFO     ] 2016-07-18 15:11:56,394 ylim = -0.250000 0.250000
yt : [INFO     ] 2016-07-18 15:11:56,396 xlim = -0.250000 0.250000
yt : [INFO     ] 2016-07-18 15:11:56,397 ylim = -0.250000 0.250000
yt : [INFO     ] 2016-07-18 15:11:56,401 Making a fixed resolution buffer of (('gas', 'kT')) 800 by 800
Out[83]:

In [84]:
ProjectionPlot(ds2, 'z', 'density', width=(500, 'kpc'), method='mip')
yt : [INFO     ] 2016-07-18 15:12:11,719 Projection completed
yt : [INFO     ] 2016-07-18 15:12:11,733 xlim = -0.250000 0.250000
yt : [INFO     ] 2016-07-18 15:12:11,734 ylim = -0.250000 0.250000
yt : [INFO     ] 2016-07-18 15:12:11,765 xlim = -0.250000 0.250000
yt : [INFO     ] 2016-07-18 15:12:11,766 ylim = -0.250000 0.250000
yt : [INFO     ] 2016-07-18 15:12:11,775 Making a fixed resolution buffer of (('gas', 'density')) 800 by 800
Out[84]:

Back to NumPy-like syntax

In [85]:
slc = ds.r[0.5,:,:] # slicing through the middle of the domain
In [86]:
p = slc.plot("density")
yt : [INFO     ] 2016-07-18 15:12:13,204 xlim = 0.000000 1.000000
yt : [INFO     ] 2016-07-18 15:12:13,205 ylim = 0.000000 1.000000
yt : [INFO     ] 2016-07-18 15:12:13,208 Making a fixed resolution buffer of (density) 800 by 800
yt : [INFO     ] 2016-07-18 15:12:13,416 Making a fixed resolution buffer of (('gas', 'density')) 800 by 800

Back to NumPy-like syntax

In [87]:
d_m = ds.r[:,0.25:0.75,0.25:0.75].integrate('density', axis='x')
yt : [INFO     ] 2016-07-18 15:12:14,421 Projection completed
In [88]:
p_d = d_m.plot()
yt : [INFO     ] 2016-07-18 15:12:14,432 xlim = 0.250000 0.750000
yt : [INFO     ] 2016-07-18 15:12:14,435 ylim = 0.250000 0.750000
yt : [INFO     ] 2016-07-18 15:12:14,438 Making a fixed resolution buffer of (('gas', 'density')) 800 by 800

Back to NumPy-like syntax

In [89]:
kT_m = ds.r[:,0.25:0.75,0.25:0.75].mean("kT", axis="x", weight="density")
yt : [INFO     ] 2016-07-18 15:12:15,178 Projection completed
In [90]:
p_kT = kT_m.plot()
yt : [INFO     ] 2016-07-18 15:12:15,185 xlim = 0.250000 0.750000
yt : [INFO     ] 2016-07-18 15:12:15,187 ylim = 0.250000 0.750000
yt : [INFO     ] 2016-07-18 15:12:15,190 Making a fixed resolution buffer of (('gas', 'kT')) 800 by 800

ParticleProjectionPlot

In [91]:
from yt import ParticleProjectionPlot
p = ParticleProjectionPlot(ds, "x", ("io","particle_mass"))
p.set_unit(('io', 'particle_mass'), 'Msun')
yt : [INFO     ] 2016-07-18 15:12:15,972 xlim = 0.000000 1.000000
yt : [INFO     ] 2016-07-18 15:12:15,973 ylim = 0.000000 1.000000
yt : [INFO     ] 2016-07-18 15:12:15,975 xlim = 0.000000 1.000000
yt : [INFO     ] 2016-07-18 15:12:15,976 ylim = 0.000000 1.000000
yt : [INFO     ] 2016-07-18 15:12:15,978 Splatting (('io', 'particle_mass')) onto a 800 by 800 mesh
Out[91]:

In [92]:
p = ProjectionPlot(ds, "x", ("deposit","all_density"))
p.show()
yt : [INFO     ] 2016-07-18 15:12:19,221 Projection completed
yt : [INFO     ] 2016-07-18 15:12:19,221 xlim = 0.000000 1.000000
yt : [INFO     ] 2016-07-18 15:12:19,222 ylim = 0.000000 1.000000
yt : [INFO     ] 2016-07-18 15:12:19,223 xlim = 0.000000 1.000000
yt : [INFO     ] 2016-07-18 15:12:19,224 ylim = 0.000000 1.000000
yt : [INFO     ] 2016-07-18 15:12:19,226 Making a fixed resolution buffer of (('deposit', 'all_density')) 800 by 800

PhasePlot and ProfilePlot

In [93]:
from yt import PhasePlot

PhasePlot(ds.all_data(), 'density', 'temperature', 'cell_mass', 
          weight_field=None, fractional=True)
Out[93]:

Analysis

  • Profiles
  • Fixed resolution buffers
  • Covering grids
  • Many more things that I don't have time to talk about in detail

Profiles

Profiles can create a 1D, 2D, or 3D histogram of a field versus a set of other fields.

We already saw a 2D histogram above when we made a PhasePlot of density, temperature, and cell mass

We can use profiles to calculate the average value or total of a field vs another field.

In [94]:
from yt import create_profile

sph = ds2.sphere(ds2.domain_center, (300., 'kpc'))

profile = create_profile(sph, ('radius'), ('density'), logs={'radius': False})
In [95]:
%matplotlib inline
import matplotlib
matplotlib.rc("font", size=18)
from matplotlib import pyplot as plt

plt.figure(figsize=(8,8))
plt.loglog(profile.x.to('kpc'), profile['density'].in_units('Msun/kpc**3'))
plt.xlabel('Radius (kpc)')
plt.ylabel(r'$\mathrm{\rho\ (M_\odot kpc^{-3})}$')
Out[95]:
<matplotlib.text.Text at 0x13aaf6be0>

Fixed Resolution Buffer

Fixed resolution buffers are a uniform resolution 2D representation of a slice or a projection.

In [96]:
slc = ds.slice("z", 0.25)
In [97]:
frb = slc.to_frb((40.0,"Mpc"), 800)
In [98]:
plt.figure(figsize=(8,8))
plt.imshow(frb["velocity_magnitude"].v, origin='lower', interpolation='nearest')
yt : [INFO     ] 2016-07-18 15:12:45,219 Making a fixed resolution buffer of (velocity_magnitude) 800 by 800
Out[98]:
<matplotlib.image.AxesImage at 0x145774438>

Covering grids

Covering grids are a uniform resolution 3D representation of a yt dataset. Very useful for analysis tools that expect a uniform resolution grid, or for simplifying analysis to avoid thinking about AMR.

In [99]:
SlicePlot(ds2, "z", 'velocity_z', center='c', width=(0.1, "unitary"))
yt : [INFO     ] 2016-07-18 15:12:46,511 xlim = -0.200000 0.200000
yt : [INFO     ] 2016-07-18 15:12:46,512 ylim = -0.200000 0.200000
yt : [INFO     ] 2016-07-18 15:12:46,513 xlim = -0.200000 0.200000
yt : [INFO     ] 2016-07-18 15:12:46,514 ylim = -0.200000 0.200000
yt : [INFO     ] 2016-07-18 15:12:46,516 Making a fixed resolution buffer of (('gas', 'velocity_z')) 800 by 800
yt : [WARNING  ] 2016-07-18 15:12:46,684 Plot image for field ('gas', 'velocity_z') has both positive and negative values. Min = -16337611.559958, Max = 14006029.747954.
yt : [WARNING  ] 2016-07-18 15:12:46,684 Switching to symlog colorbar scaling unless linear scaling is specified later
Out[99]:

In [100]:
# create covering grid of z-velocity at AMR level 3
cgrid = ds2.covering_grid(3, left_edge=[-0.15, -0.15, -0.15], dims=[128, 128, 128])
velocity_field = cgrid["velocity_z"]

print (velocity_field.shape)

plt.figure(figsize=(8,8))
plt.imshow(velocity_field[:,:,70].T.v, interpolation='none', origin='lower')
plt.show()
(128, 128, 128)

Analysis Modules

In [101]:
IFrame("http://yt-project.org/docs/dev/analyzing/analysis_modules/index.html", width=700, height=600)
Out[101]:

Volume Rendering

In [102]:
IFrame("http://yt-project.org/docs/dev/visualizing/volume_rendering.html", width=700, height=600)
Out[102]:

What about...?

Q: What about Athena++?

A: Support for Athena++ is under development, and it mostly works. To get it now, come ask me after the talk or later this week. It will hopefully be merged into the main development branch soon.

What about...?

Q: What if I want to look at the data from the perspective of the way it's laid out on the mesh?

A: You can definitely do that. Each dataset has an index object that holds the mesh hierarchy:

In [103]:
print(ds.index.grids)
[EnzoGrid_0001 EnzoGrid_0002 EnzoGrid_0003 EnzoGrid_0004 EnzoGrid_0005
 EnzoGrid_0006 EnzoGrid_0007 EnzoGrid_0008 EnzoGrid_0009 EnzoGrid_0010
 EnzoGrid_0011 EnzoGrid_0012 EnzoGrid_0013 EnzoGrid_0014 EnzoGrid_0015
 EnzoGrid_0016 EnzoGrid_0017 EnzoGrid_0018 EnzoGrid_0019 EnzoGrid_0020
 EnzoGrid_0021 EnzoGrid_0022 EnzoGrid_0023 EnzoGrid_0024 EnzoGrid_0025
 EnzoGrid_0026 EnzoGrid_0027 EnzoGrid_0028 EnzoGrid_0029 EnzoGrid_0030
 EnzoGrid_0031 EnzoGrid_0032 EnzoGrid_0033 EnzoGrid_0034 EnzoGrid_0035
 EnzoGrid_0036 EnzoGrid_0037 EnzoGrid_0038 EnzoGrid_0039 EnzoGrid_0040
 EnzoGrid_0041 EnzoGrid_0042 EnzoGrid_0043 EnzoGrid_0044 EnzoGrid_0045
 EnzoGrid_0046 EnzoGrid_0047 EnzoGrid_0048 EnzoGrid_0049 EnzoGrid_0050
 EnzoGrid_0051 EnzoGrid_0052 EnzoGrid_0053 EnzoGrid_0054 EnzoGrid_0055
 EnzoGrid_0056 EnzoGrid_0057 EnzoGrid_0058 EnzoGrid_0059 EnzoGrid_0060
 EnzoGrid_0061 EnzoGrid_0062 EnzoGrid_0063 EnzoGrid_0064 EnzoGrid_0065
 EnzoGrid_0066 EnzoGrid_0067 EnzoGrid_0068 EnzoGrid_0069 EnzoGrid_0070
 EnzoGrid_0071 EnzoGrid_0072 EnzoGrid_0073 EnzoGrid_0074 EnzoGrid_0075
 EnzoGrid_0076 EnzoGrid_0077 EnzoGrid_0078 EnzoGrid_0079 EnzoGrid_0080
 EnzoGrid_0081 EnzoGrid_0082 EnzoGrid_0083 EnzoGrid_0084 EnzoGrid_0085
 EnzoGrid_0086 EnzoGrid_0087 EnzoGrid_0088 EnzoGrid_0089 EnzoGrid_0090
 EnzoGrid_0091 EnzoGrid_0092 EnzoGrid_0093 EnzoGrid_0094 EnzoGrid_0095
 EnzoGrid_0096 EnzoGrid_0097 EnzoGrid_0098 EnzoGrid_0099 EnzoGrid_0100
 EnzoGrid_0101 EnzoGrid_0102 EnzoGrid_0103 EnzoGrid_0104 EnzoGrid_0105
 EnzoGrid_0106 EnzoGrid_0107 EnzoGrid_0108 EnzoGrid_0109 EnzoGrid_0110
 EnzoGrid_0111 EnzoGrid_0112 EnzoGrid_0113 EnzoGrid_0114 EnzoGrid_0115
 EnzoGrid_0116 EnzoGrid_0117 EnzoGrid_0118 EnzoGrid_0119 EnzoGrid_0120
 EnzoGrid_0121 EnzoGrid_0122 EnzoGrid_0123 EnzoGrid_0124]
In [104]:
print(ds.index.grids[11].LeftEdge)
print(ds.index.grids[11].RightEdge)
print(ds.index.grids[11].ActiveDimensions)
print(ds.index.grids[11]['density'])
[ 0.40625  0.15625  0.625  ] code_length
[ 0.4375   0.25     0.65625] code_length
[2 6 2]
[[[  1.35611167e-31   1.28741342e-31]
  [  1.71169103e-31   1.57911010e-31]
  [  2.15770820e-31   2.16331829e-31]
  [  2.14562925e-31   2.18296743e-31]
  [  1.81851634e-31   1.56691138e-31]
  [  1.47996968e-31   1.23395276e-31]]

 [[  1.22126042e-31   1.26510572e-31]
  [  1.78934557e-31   1.67484841e-31]
  [  2.24622361e-31   2.18491079e-31]
  [  2.23558405e-31   2.18519381e-31]
  [  1.81162540e-31   1.37081000e-31]
  [  1.21279277e-31   1.08580363e-31]]] g/cm**3

What about...?

Q: My favorite code doesn't appear to be supported. Can I still use yt?

A: Yes! For a start, we have ways of loading in generic array data:

New Features in yt 3.3

  • First-class support for unstructured meshes
  • Improved volume rendering interface
  • Interactive OpenGL volume rendering
  • Easy conversions between different unit systems
  • "NumPy-like" operations for data objects

Coming soon

  • Improved support for non-cartesian geometries (spherical, cylindrical, PPV-cube, etc.)
  • Improved scaling for datasets with particles
  • Streamlining the process to get data into yt

Community

  • Over 20,000 changesets since 2007 by over 100 unique contributors
  • 350 subscribers to users mailing list, 120 subscribers to developer mailing list
  • Funding for aspects of yt development comes from the NSF, NASA, and the Gordon and Betty Moore Foundation

Getting involved and asking for help

Documentation: http://yt-project.org/doc

Users mailing list: http://lists.spacepope.org/listinfo.cgi/yt-users-spacepope.org

Developer mailing list: http://lists.spacepope.org/listinfo.cgi/yt-dev-spacepope.org

Developer guide: http://yt-project.org/docs/dev/developing/index.html

Slack Channel: https://yt-project.slack.com

IRC Channel: http://yt-project.org/irc.html (or #yt on freenode with your favorite IRC client)