[yt-svn] commit/yt: 65 new changesets
commits-noreply at bitbucket.org
commits-noreply at bitbucket.org
Thu Nov 6 07:33:43 PST 2014
65 new commits in yt:
https://bitbucket.org/yt_analysis/yt/commits/3b7b51950d92/
Changeset: 3b7b51950d92
Branch: yt
User: jzuhone
Date: 2014-10-06 18:13:09+00:00
Summary: Allow for the spectral axis to be converted to units other than velocity.
Affected #: 1 file
diff -r 4cf5c5369df9b012759ab979330d8e356d1b0e00 -r 3b7b51950d924c7cb4ddd2aedf8af7c2822b9f7b yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -16,6 +16,8 @@
from yt.utilities.fits_image import FITSImageBuffer
from yt.visualization.volume_rendering.camera import off_axis_projection
from yt.funcs import get_pbar
+from yt.utilities.physical_constants import clight
+import yt.units.dimensions as ytdims
def create_vlos(z_hat):
def _v_los(field, data):
@@ -25,9 +27,14 @@
return -vz
return _v_los
+fits_info = {"velocity":("m/s","VELOCITY"),
+ "frequency":("Hz","FREQUENCY"),
+ "energy":("eV","ENERGY"),
+ "wavelength":("angstrom","WAVELENG")}
+
class PPVCube(object):
def __init__(self, ds, normal, field, width=(1.0,"unitary"),
- dims=(100,100,100), velocity_bounds=None):
+ dims=(100,100,100), velocity_bounds=None, rest_freq=None):
r""" Initialize a PPVCube object.
Parameters
@@ -47,6 +54,11 @@
A 3-tuple of (vmin, vmax, units) for the velocity bounds to
integrate over. If None, the largest velocity of the
dataset will be used, e.g. velocity_bounds = (-v.max(), v.max())
+ rest_freq : tuple, optional
+ A (value, unit) tuple indicating the "rest frequency" of the spectral
+ axis of the PPV cube. The spectral axis will be converted to the units and
+ displaced by the value. Can be in units of energy, wavelength, or frequency.
+ If not set the default is to leave the spectral axis in velocity units.
Examples
--------
@@ -58,6 +70,7 @@
self.ds = ds
self.field = field
self.width = width
+ self.rest_freq = rest_freq
self.nx = dims[0]
self.ny = dims[1]
@@ -86,10 +99,10 @@
self.vbins = np.linspace(self.v_bnd[0], self.v_bnd[1], num=self.nv+1)
self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
- self.dv = (self.v_bnd[1]-self.v_bnd[0])/self.nv
+ self.dv = self.v_bins[1]-self.v_bins[0]
_vlos = create_vlos(orient.unit_vectors[2])
- ds.field_info.add_field(("gas","v_los"), function=_vlos, units="cm/s")
+ self.ds.field_info.add_field(("gas","v_los"), function=_vlos, units="cm/s")
self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.field_units)
pbar = get_pbar("Generating cube.", self.nv)
@@ -104,6 +117,13 @@
pbar.finish()
+ if self.rest_freq is not None:
+ # If we want units other than velocity, we re-calculate these quantities
+ self.rest_freq = self.ds.quan(rest_freq[0], rest_freq[1])
+ self.vbins = self.rest_freq*(1.-self.vbins.in_cgs()/clight)
+ self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
+ self.dv = self.v_bins[1]-self.v_bins[0]
+
def write_fits(self, filename, clobber=True, length_unit=(10.0, "kpc"),
sky_center=(30.,45.)):
r""" Write the PPVCube to a FITS file.
@@ -132,11 +152,25 @@
center = [0.0,0.0]
types = ["LINEAR","LINEAR"]
- v_center = 0.5*(self.v_bnd[0]+self.v_bnd[1]).in_units("m/s").value
+ dims = self.dv.units.dimensions
+
+ if dims == ytdims.rate:
+ axis_type = "frequency"
+ elif dims == ytdims.length:
+ axis_type = "wavelength"
+ elif dims == ytdims.energy:
+ axis_type = "energy"
+ elif dims == ytdims.velocity:
+ axis_type = "velocity"
+
+ vunit = fits_info[axis_type]
+ vtype = fits_info[axis_type]
+
+ v_center = 0.5*(self.v_bnd[0]+self.v_bnd[1]).in_units(vunit).value
dx = length_unit[0]/self.nx
dy = length_unit[0]/self.ny
- dv = self.dv.in_units("m/s").value
+ dv = self.dv.in_units(vunit).value
if length_unit[1] == "deg":
dx *= -1.
@@ -145,8 +179,8 @@
w.wcs.crpix = [0.5*(self.nx+1), 0.5*(self.ny+1), 0.5*(self.nv+1)]
w.wcs.cdelt = [dx,dy,dv]
w.wcs.crval = [center[0], center[1], v_center]
- w.wcs.cunit = [length_unit[1],length_unit[1],"m/s"]
- w.wcs.ctype = [types[0],types[1],"VELO-LSR"]
+ w.wcs.cunit = [length_unit[1],length_unit[1],vunit]
+ w.wcs.ctype = [types[0],types[1],vtype]
fib = FITSImageBuffer(self.data.transpose(), fields=self.field, wcs=w)
fib[0].header["bunit"] = self.field_units
https://bitbucket.org/yt_analysis/yt/commits/24665f82c66e/
Changeset: 24665f82c66e
Branch: yt
User: jzuhone
Date: 2014-10-06 20:13:16+00:00
Summary: Attempting to integrate thermal broadening. Also some bug fixes.
Affected #: 1 file
diff -r 3b7b51950d924c7cb4ddd2aedf8af7c2822b9f7b -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -16,7 +16,7 @@
from yt.utilities.fits_image import FITSImageBuffer
from yt.visualization.volume_rendering.camera import off_axis_projection
from yt.funcs import get_pbar
-from yt.utilities.physical_constants import clight
+from yt.utilities.physical_constants import clight, mh, kboltz
import yt.units.dimensions as ytdims
def create_vlos(z_hat):
@@ -34,7 +34,8 @@
class PPVCube(object):
def __init__(self, ds, normal, field, width=(1.0,"unitary"),
- dims=(100,100,100), velocity_bounds=None, rest_freq=None):
+ dims=(100,100,100), velocity_bounds=None, rest_value=None,
+ ion_weight=None):
r""" Initialize a PPVCube object.
Parameters
@@ -54,11 +55,14 @@
A 3-tuple of (vmin, vmax, units) for the velocity bounds to
integrate over. If None, the largest velocity of the
dataset will be used, e.g. velocity_bounds = (-v.max(), v.max())
- rest_freq : tuple, optional
- A (value, unit) tuple indicating the "rest frequency" of the spectral
+ rest_value : tuple, optional
+ A (value, unit) tuple indicating the rest value of the spectral
axis of the PPV cube. The spectral axis will be converted to the units and
displaced by the value. Can be in units of energy, wavelength, or frequency.
If not set the default is to leave the spectral axis in velocity units.
+ ion_weight : float, optional
+ Set this value to the atomic weight of the ion that is emitting the line
+ in order to include thermal broadening.
Examples
--------
@@ -70,7 +74,7 @@
self.ds = ds
self.field = field
self.width = width
- self.rest_freq = rest_freq
+ self.rest_value = rest_value
self.nx = dims[0]
self.ny = dims[1]
@@ -99,7 +103,14 @@
self.vbins = np.linspace(self.v_bnd[0], self.v_bnd[1], num=self.nv+1)
self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
- self.dv = self.v_bins[1]-self.v_bins[0]
+ self.dv = self.vbins[1]-self.vbins[0]
+
+ if ion_weight is None:
+ self.ion_mass = mh
+ self.phi_th = lambda v, v_th: 1.0
+ else:
+ self.ion_mass = ion_weight*mh
+ self.phi_th = lambda v, v_th: self.dv/(np.sqrt(np.pi)*v_th)*np.exp(-(v/v_th)**2)
_vlos = create_vlos(orient.unit_vectors[2])
self.ds.field_info.add_field(("gas","v_los"), function=_vlos, units="cm/s")
@@ -117,10 +128,10 @@
pbar.finish()
- if self.rest_freq is not None:
+ if self.rest_value is not None:
# If we want units other than velocity, we re-calculate these quantities
- self.rest_freq = self.ds.quan(rest_freq[0], rest_freq[1])
- self.vbins = self.rest_freq*(1.-self.vbins.in_cgs()/clight)
+ self.rest_value = self.ds.quan(rest_value[0], rest_value[1])
+ self.vbins = self.rest_value*(1.-self.vbins.in_cgs()/clight)
self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
self.dv = self.v_bins[1]-self.v_bins[0]
@@ -166,7 +177,7 @@
vunit = fits_info[axis_type]
vtype = fits_info[axis_type]
- v_center = 0.5*(self.v_bnd[0]+self.v_bnd[1]).in_units(vunit).value
+ v_center = 0.5*(self.v_bins[0]+self.v_bins[-1]).in_units(vunit).value
dx = length_unit[0]/self.nx
dy = length_unit[0]/self.ny
@@ -191,8 +202,9 @@
def _create_intensity(self, i):
def _intensity(field, data):
vlos = data["v_los"]
+ v_th = np.sqrt(2*kboltz*data["temperature"]/self.ion_mass)
w = np.abs(vlos-self.vmid[i])/self.dv.in_units(vlos.units)
w = 1.-w
w[w < 0.0] = 0.0
- return data[self.field]*w
+ return data[self.field]*self.phi_th(vlos, v_th)*w
return _intensity
https://bitbucket.org/yt_analysis/yt/commits/7181ddfcb573/
Changeset: 7181ddfcb573
Branch: yt
User: jzuhone
Date: 2014-10-06 20:13:51+00:00
Summary: Merge
Affected #: 29 files
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 doc/install_script.sh
--- a/doc/install_script.sh
+++ b/doc/install_script.sh
@@ -986,8 +986,10 @@
if !( ( ${DEST_DIR}/bin/python2.7 -c "import readline" 2>&1 )>> ${LOG_FILE})
then
- echo "Installing pure-python readline"
- ( ${DEST_DIR}/bin/pip install readline 2>&1 ) 1>> ${LOG_FILE}
+ if !( ( ${DEST_DIR}/bin/python2.7 -c "import gnureadline" 2>&1 )>> ${LOG_FILE})
+ echo "Installing pure-python readline"
+ ( ${DEST_DIR}/bin/pip install gnureadline 2>&1 ) 1>> ${LOG_FILE}
+ fi
fi
if [ $INST_ENZO -eq 1 ]
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 doc/source/analyzing/analysis_modules/PPVCube.ipynb
--- a/doc/source/analyzing/analysis_modules/PPVCube.ipynb
+++ b/doc/source/analyzing/analysis_modules/PPVCube.ipynb
@@ -291,7 +291,7 @@
"cell_type": "code",
"collapsed": false,
"input": [
- "prj = yt.ProjectionPlot(ds, \"z\", [\"density\"], proj_style=\"sum\")\n",
+ "prj = yt.ProjectionPlot(ds, \"z\", [\"density\"], method=\"sum\")\n",
"prj.set_log(\"density\", True)\n",
"prj.set_zlim(\"density\", 1.0e-3, 0.2)\n",
"prj.show()"
@@ -304,4 +304,4 @@
"metadata": {}
}
]
-}
\ No newline at end of file
+}
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 doc/source/analyzing/objects.rst
--- a/doc/source/analyzing/objects.rst
+++ b/doc/source/analyzing/objects.rst
@@ -225,12 +225,12 @@
**Projection**
| Class :class:`~yt.data_objects.construction_data_containers.YTQuadTreeProjBase`
- | Usage: ``proj(field, axis, weight_field=None, center=None, ds=None, data_source=None, style="integrate", field_parameters=None)``
+ | Usage: ``proj(field, axis, weight_field=None, center=None, ds=None, data_source=None, method="integrate", field_parameters=None)``
| A 2D projection of a 3D volume along one of the axis directions.
By default, this is a line integral through the entire simulation volume
(although it can be a subset of that volume specified by a data object
with the ``data_source`` keyword). Alternatively, one can specify
- a weight_field and different ``style`` values to change the nature
+ a weight_field and different ``method`` values to change the nature
of the projection outcome. See :ref:`projection-types` for more information.
**Streamline**
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 doc/source/cookbook/fits_radio_cubes.ipynb
--- a/doc/source/cookbook/fits_radio_cubes.ipynb
+++ b/doc/source/cookbook/fits_radio_cubes.ipynb
@@ -183,14 +183,14 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "We can also make a projection of all the emission along the line of sight. Since we're not doing an integration along a path length, we needed to specify `proj_style = \"sum\"`:"
+ "We can also make a projection of all the emission along the line of sight. Since we're not doing an integration along a path length, we needed to specify `method = \"sum\"`:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
- "prj = yt.ProjectionPlot(ds, \"z\", [\"intensity\"], proj_style=\"sum\", origin=\"native\")\n",
+ "prj = yt.ProjectionPlot(ds, \"z\", [\"intensity\"], method=\"sum\", origin=\"native\")\n",
"prj.show()"
],
"language": "python",
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 doc/source/cookbook/fits_xray_images.ipynb
--- a/doc/source/cookbook/fits_xray_images.ipynb
+++ b/doc/source/cookbook/fits_xray_images.ipynb
@@ -264,7 +264,7 @@
" [\"flux\",\"projected_temperature\",\"pseudo_pressure\",\"pseudo_entropy\"], \n",
" origin=\"native\", field_parameters={\"exposure_time\":exposure_time},\n",
" data_source=circle_reg,\n",
- " proj_style=\"sum\")\n",
+ " method=\"sum\")\n",
"prj.set_log(\"flux\",True)\n",
"prj.set_log(\"pseudo_pressure\",False)\n",
"prj.set_log(\"pseudo_entropy\",False)\n",
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 doc/source/cookbook/free_free_field.py
--- a/doc/source/cookbook/free_free_field.py
+++ b/doc/source/cookbook/free_free_field.py
@@ -6,6 +6,7 @@
# Need to grab the proton mass from the constants database
from yt.utilities.physical_constants import mp
+exit()
# Define the emission field
keVtoerg = 1.602e-9 # Convert energy in keV to energy in erg
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 doc/source/cookbook/hse_field.py
--- a/doc/source/cookbook/hse_field.py
+++ b/doc/source/cookbook/hse_field.py
@@ -1,163 +1,35 @@
-### THIS RECIPE IS CURRENTLY BROKEN IN YT-3.0
-### DO NOT TRUST THIS RECIPE UNTIL THIS LINE IS REMOVED
-
import numpy as np
import yt
+from yt.fields.field_plugin_registry import \
+ register_field_plugin
+from yt.fields.fluid_fields import \
+ setup_gradient_fields
+
+
# Define the components of the gravitational acceleration vector field by
# taking the gradient of the gravitational potential
-
- at yt.derived_field(name='gravitational_acceleration_x',
- units='cm/s**2', take_log=False,
- validators=[yt.ValidateSpatial(1,["gravitational_potential"])])
-def gravitational_acceleration_x(field, data):
-
- # We need to set up stencils
-
- sl_left = slice(None, -2, None)
- sl_right = slice(2, None, None)
- div_fac = 2.0
-
- dx = div_fac * data['dx'][0]
-
- gx = data["gravitational_potential"][sl_right, 1:-1, 1:-1]/dx
- gx -= data["gravitational_potential"][sl_left, 1:-1, 1:-1]/dx
-
- new_field = np.zeros(data["gravitational_potential"].shape,
- dtype='float64')*gx.uq
- new_field[1:-1, 1:-1, 1:-1] = -gx
-
- return new_field
-
-
- at yt.derived_field(name='gravitational_acceleration_y',
- units='cm/s**2', take_log=False,
- validators=[yt.ValidateSpatial(1,["gravitational_potential"])])
-def gravitational_acceleration_y(field, data):
-
- # We need to set up stencils
-
- sl_left = slice(None, -2, None)
- sl_right = slice(2, None, None)
- div_fac = 2.0
-
- dy = div_fac * data['dy'].flatten()[0]
-
- gy = data["gravitational_potential"][1:-1, sl_right, 1:-1]/dy
- gy -= data["gravitational_potential"][1:-1, sl_left, 1:-1]/dy
-
- new_field = np.zeros(data["gravitational_potential"].shape,
- dtype='float64')*gy.uq
-
- new_field[1:-1, 1:-1, 1:-1] = -gy
-
- return new_field
-
-
- at yt.derived_field(name='gravitational_acceleration_z',
- units='cm/s**2', take_log=False,
- validators=[yt.ValidateSpatial(1,["gravitational_potential"])])
-def gravitational_acceleration_z(field, data):
-
- # We need to set up stencils
-
- sl_left = slice(None, -2, None)
- sl_right = slice(2, None, None)
- div_fac = 2.0
-
- dz = div_fac * data['dz'].flatten()[0]
-
- gz = data["gravitational_potential"][1:-1, 1:-1, sl_right]/dz
- gz -= data["gravitational_potential"][1:-1, 1:-1, sl_left]/dz
-
- new_field = np.zeros(data["gravitational_potential"].shape,
- dtype='float64')*gz.uq
- new_field[1:-1, 1:-1, 1:-1] = -gz
-
- return new_field
-
-
-# Define the components of the pressure gradient field
-
-
- at yt.derived_field(name='grad_pressure_x', units='g/(cm*s)**2', take_log=False,
- validators=[yt.ValidateSpatial(1,["pressure"])])
-def grad_pressure_x(field, data):
-
- # We need to set up stencils
-
- sl_left = slice(None, -2, None)
- sl_right = slice(2, None, None)
- div_fac = 2.0
-
- dx = div_fac * data['dx'].flatten()[0]
-
- px = data["pressure"][sl_right, 1:-1, 1:-1]/dx
- px -= data["pressure"][sl_left, 1:-1, 1:-1]/dx
-
- new_field = np.zeros(data["pressure"].shape, dtype='float64')*px.uq
- new_field[1:-1, 1:-1, 1:-1] = px
-
- return new_field
-
-
- at yt.derived_field(name='grad_pressure_y', units='g/(cm*s)**2', take_log=False,
- validators=[yt.ValidateSpatial(1,["pressure"])])
-def grad_pressure_y(field, data):
-
- # We need to set up stencils
-
- sl_left = slice(None, -2, None)
- sl_right = slice(2, None, None)
- div_fac = 2.0
-
- dy = div_fac * data['dy'].flatten()[0]
-
- py = data["pressure"][1:-1, sl_right, 1:-1]/dy
- py -= data["pressure"][1:-1, sl_left, 1:-1]/dy
-
- new_field = np.zeros(data["pressure"].shape, dtype='float64')*py.uq
- new_field[1:-1, 1:-1, 1:-1] = py
-
- return new_field
-
-
- at yt.derived_field(name='grad_pressure_z', units='g/(cm*s)**2', take_log=False,
- validators=[yt.ValidateSpatial(1,["pressure"])])
-def grad_pressure_z(field, data):
-
- # We need to set up stencils
-
- sl_left = slice(None, -2, None)
- sl_right = slice(2, None, None)
- div_fac = 2.0
-
- dz = div_fac * data['dz'].flatten()[0]
-
- pz = data["pressure"][1:-1, 1:-1, sl_right]/dz
- pz -= data["pressure"][1:-1, 1:-1, sl_left]/dz
-
- new_field = np.zeros(data["pressure"].shape, dtype='float64')*pz.uq
- new_field[1:-1, 1:-1, 1:-1] = pz
-
- return new_field
-
+ at register_field_plugin
+def setup_my_fields(registry, ftype="gas", slice_info=None):
+ setup_gradient_fields(registry, (ftype, "gravitational_potential"),
+ "cm ** 2 / s ** 2", slice_info)
# Define the "degree of hydrostatic equilibrium" field
+
@yt.derived_field(name='HSE', units=None, take_log=False,
display_name='Hydrostatic Equilibrium')
def HSE(field, data):
- gx = data["density"]*data["gravitational_acceleration_x"]
- gy = data["density"]*data["gravitational_acceleration_y"]
- gz = data["density"]*data["gravitational_acceleration_z"]
+ gx = data["density"] * data["gravitational_potential_gradient_x"]
+ gy = data["density"] * data["gravitational_potential_gradient_y"]
+ gz = data["density"] * data["gravitational_potential_gradient_z"]
- hx = data["grad_pressure_x"] - gx
- hy = data["grad_pressure_y"] - gy
- hz = data["grad_pressure_z"] - gz
+ hx = data["pressure_gradient_x"] - gx
+ hy = data["pressure_gradient_y"] - gy
+ hz = data["pressure_gradient_z"] - gz
- h = np.sqrt((hx*hx+hy*hy+hz*hz)/(gx*gx+gy*gy+gz*gz))
+ h = np.sqrt((hx * hx + hy * hy + hz * hz) / (gx * gx + gy * gy + gz * gz))
return h
@@ -166,6 +38,10 @@
ds = yt.load("GasSloshingLowRes/sloshing_low_res_hdf5_plt_cnt_0350")
+# gradient operator requires periodic boundaries. This dataset has
+# open boundary conditions. We need to hack it for now (this will be fixed
+# in future version of yt)
+ds.periodicity = (True, True, True)
# Take a slice through the center of the domain
slc = yt.SlicePlot(ds, 2, ["density", "HSE"], width=(1, 'Mpc'))
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 doc/source/cookbook/tests/test_cookbook.py
--- a/doc/source/cookbook/tests/test_cookbook.py
+++ b/doc/source/cookbook/tests/test_cookbook.py
@@ -11,19 +11,30 @@
"""
import glob
import os
-import sys
+import subprocess
-sys.path.append(os.path.join(os.getcwd(), "doc/source/cookbook"))
+
+PARALLEL_TEST = {"rockstar_nest.py": "3"}
def test_recipe():
'''Dummy test grabbing all cookbook's recipes'''
for fname in glob.glob("doc/source/cookbook/*.py"):
- module_name = os.path.splitext(os.path.basename(fname))[0]
- yield check_recipe, module_name
+ recipe = os.path.basename(fname)
+ check_recipe.description = "Testing recipe: %s" % recipe
+ if recipe in PARALLEL_TEST:
+ yield check_recipe, \
+ ["mpiexec", "-n", PARALLEL_TEST[recipe], "python", fname]
+ else:
+ yield check_recipe, ["python", fname]
-def check_recipe(module_name):
+def check_recipe(cmd):
'''Run single recipe'''
- __import__(module_name)
- assert True
+ try:
+ subprocess.check_call(cmd)
+ result = True
+ except subprocess.CalledProcessError, e:
+ print("Stdout output:\n", e.output)
+ result = False
+ assert result
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 doc/source/developing/building_the_docs.rst
--- a/doc/source/developing/building_the_docs.rst
+++ b/doc/source/developing/building_the_docs.rst
@@ -105,7 +105,10 @@
You will need to have the yt repository available on your computer, which
is done by default if you have yt installed. In addition, you need a
-current version of Sphinx_ (1.1.3) documentation software installed.
+current version of Sphinx_ (1.1.3) documentation software installed, as
+well as the Sphinx
+`Bootstrap theme <https://pypi.python.org/pypi/sphinx-bootstrap-theme/>`_,
+which can be installed via ``pip install sphinx_bootstrap_theme``.
In order to tell sphinx not to do all of the dynamical building, you must
set the ``$READTHEDOCS`` environment variable to be True by typing this at
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 doc/source/developing/developing.rst
--- a/doc/source/developing/developing.rst
+++ b/doc/source/developing/developing.rst
@@ -173,7 +173,7 @@
If you plan to develop yt on Windows, it is necessary to use the `MinGW
<http://www.mingw.org/>`_ gcc compiler that can be installed using the `Anaconda
Python Distribution <https://store.continuum.io/cshop/anaconda/>`_. The libpython package must be
- installed from Anaconda as well. These can both be installed with a single command:
+installed from Anaconda as well. These can both be installed with a single command:
.. code-block:: bash
@@ -229,6 +229,19 @@
If you end up doing considerable development, you can set an alias in the
file ``.hg/hgrc`` to point to this path.
+
+ .. note::
+ Note that the above approach uses HTTPS as the transfer protocol
+ between your machine and BitBucket. If you prefer to use SSH - or
+ perhaps you're behind a proxy that doesn't play well with SSL via
+ HTTPS - you may want to set up an `SSH key`_ on BitBucket. Then, you use
+ the syntax ``ssh://hg@bitbucket.org/YourUsername/yt``, or equivalent, in
+ place of ``https://bitbucket.org/YourUsername/yt`` in Mercurial commands.
+ For consistency, all commands we list in this document will use the HTTPS
+ protocol.
+
+ .. _SSH key: https://confluence.atlassian.com/display/BITBUCKET/Set+up+SSH+for+Mercurial
+
#. Issue a pull request at
https://bitbucket.org/YourUsername/yt/pull-request/new
@@ -246,6 +259,88 @@
#. Your pull request will be automatically updated.
+.. _multiple-PRs:
+
+Working with Multiple BitBucket Pull Requests
++++++++++++++++++++++++++++++++++++++++++++++
+
+Once you become active developing for yt, you may be working on
+various aspects of the code or bugfixes at the same time. Currently,
+BitBucket's *modus operandi* for pull requests automatically updates
+your active pull request with every ``hg push`` of commits that are a
+descendant of the head of your pull request. In a normal workflow,
+this means that if you have an active pull request, make some changes
+locally for, say, an unrelated bugfix, then push those changes back to
+your fork in the hopes of creating a *new* pull request, you'll
+actually end up updating your current pull request!
+
+There are a few ways around this feature of BitBucket that will allow
+for multiple pull requests to coexist; we outline one such method
+below. We assume that you have a fork of yt at
+``http://bitbucket.org/YourUsername/Your_yt`` (see
+:ref:`sharing-changes` for instructions on creating a fork) and that
+you have an active pull request to the main repository.
+
+The main issue with starting another pull request is to make sure that
+your push to BitBucket doesn't go to the same head as your
+existing pull request and trigger BitBucket's auto-update feature.
+Here's how to get your local repository away from your current pull
+request head using `revsets <http://www.selenic.com/hg/help/revsets>`_
+and your ``hgrc`` file:
+
+#. Set up a Mercurial path for the main yt repository (note this is a convenience
+ step and only needs to be done once). Add the following to your
+ ``Your_yt/.hg/hgrc``::
+
+ [paths]
+ upstream = https://bitbucket.org/yt_analysis/yt
+
+ This will create a path called ``upstream`` that is aliased to the URL of the
+ main yt repository.
+#. Now we'll use revsets_ to update your local repository to the tip of the
+ ``upstream`` path:
+
+ .. code-block:: bash
+
+ $ hg pull upstream
+ $ hg update -r "remote(tip,'upstream')"
+
+After the above steps, your local repository should be at the tip of
+the main yt repository. If you find yourself doing this a lot, it may
+be worth aliasing this task in your ``hgrc`` file by adding something like::
+
+ [alias]
+ myupdate = update -r "remote(tip,'upstream')"
+
+And then you can just issue ``hg myupdate`` to get at the tip of the main yt repository.
+
+Make sure you are on the branch you want to be on, and then you can
+make changes and ``hg commit`` them. If you prefer working with
+`bookmarks <http://mercurial.selenic.com/wiki/Bookmarks>`_, you may
+want to make a bookmark before committing your changes, such as ``hg
+bookmark mybookmark``.
+
+To push to your fork on BitBucket if you didn't use a bookmark, you issue the following:
+
+.. code-block:: bash
+
+ $ hg push -r . -f https://bitbucket.org/YourUsername/Your_yt
+
+The ``-r .`` means "push only the commit I'm standing on and any ancestors." The
+``-f`` is to force Mecurial to do the push since we are creating a new remote head.
+
+Note that if you *did* use a bookmark, you don't have to force the push, but you do
+need to push the bookmark; in other words do the following instead of the above:
+
+.. code-block:: bash
+
+ $ hg push -B mybookmark https://bitbucket.org/YourUsername/Your_yt
+
+The ``-B`` means "publish my bookmark and any relevant changesets to the remote server."
+
+You can then go to the BitBucket interface and issue a new pull request based on
+your last changes, as usual.
+
How To Get The Source Code For Editing
--------------------------------------
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -44,6 +44,16 @@
<tr valign="top"><td width="25%"><p>
+ <a href="examining/index.html">Loading and Examining Data</a>
+ </p>
+ </td>
+ <td width="75%">
+ <p class="linkdescr">How to load and examine all dataset types in yt</p>
+ </td>
+ </tr>
+ <tr valign="top">
+ <td width="25%">
+ <p><a href="yt3differences.html">yt 3.0</a></p></td>
@@ -84,16 +94,6 @@
<tr valign="top"><td width="25%"><p>
- <a href="examining/index.html">Examining Data</a>
- </p>
- </td>
- <td width="75%">
- <p class="linkdescr">Load data and directly access raw values for low-level analysis</p>
- </td>
- </tr>
- <tr valign="top">
- <td width="25%">
- <p><a href="developing/index.html">Developing in yt</a></p></td>
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -239,13 +239,13 @@
Types of Projections
""""""""""""""""""""
-There are several different styles of projections that can be made either
+There are several different methods of projections that can be made either
when creating a projection with ds.proj() or when making a ProjectionPlot.
-In either construction method, set the ``style`` keyword to be one of the
+In either construction method, set the ``method`` keyword to be one of the
following:
``integrate`` (unweighted)
- This is the default projection style. It simply integrates the
+ This is the default projection method. It simply integrates the
requested field :math:`f(x)` along a line of sight :math:`\hat{n}` ,
given by the axis parameter (e.g. :math:`\hat{i},\hat{j},` or
:math:`\hat{k}`). The units of the projected field
@@ -258,7 +258,7 @@
g(X) = {\int\ {f(x)\hat{n}\cdot{dx}}}
``integrate`` (weighted)
- When using the ``integrate`` style, a ``weight_field`` argument may also
+ When using the ``integrate`` method, a ``weight_field`` argument may also
be specified, which will produce a weighted projection. :math:`w(x)`
is the field used as a weight. One common example would
be to weight the "temperature" field by the "density" field. In this case,
@@ -269,15 +269,15 @@
g(X) = \frac{\int\ {f(x)w(x)\hat{n}\cdot{dx}}}{\int\ {w(x)\hat{n}\cdot{dx}}}
``mip``
- This style picks out the maximum value of a field along the line of
+ This method picks out the maximum value of a field along the line of
sight given by the axis parameter.
``sum``
- This style is the same as ``integrate``, except that it does not
+ This method is the same as ``integrate``, except that it does not
multiply by a path length when performing the integration, and is just a
straight summation of the field along the given axis. The units of the
projected field will be the same as those of the unprojected field. This
- style is typically only useful for datasets such as 3D FITS cubes where
+ method is typically only useful for datasets such as 3D FITS cubes where
the third axis of the dataset is something like velocity or frequency.
.. _off-axis-projections:
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 yt/data_objects/construction_data_containers.py
--- a/yt/data_objects/construction_data_containers.py
+++ b/yt/data_objects/construction_data_containers.py
@@ -186,11 +186,14 @@
data_source : `yt.data_objects.api.AMRData`, optional
If specified, this will be the data source used for selecting
regions to project.
- style : string, optional
- The style of projection to be performed.
+ method : string, optional
+ The method of projection to be performed.
"integrate" : integration along the axis
"mip" : maximum intensity projection
"sum" : same as "integrate", except that we don't multiply by the path length
+ style : string, optional
+ The same as the method keyword. Deprecated as of version 3.0.2.
+ Please use method keyword instead.
field_parameters : dict of items
Values to be passed as field parameters that can be
accessed by generated fields.
@@ -208,20 +211,26 @@
_container_fields = ('px', 'py', 'pdx', 'pdy', 'weight_field')
def __init__(self, field, axis, weight_field = None,
center = None, ds = None, data_source = None,
- style = "integrate", field_parameters = None):
+ style = None, method = "integrate",
+ field_parameters = None):
YTSelectionContainer2D.__init__(self, axis, ds, field_parameters)
- if style == "sum":
- self.proj_style = "integrate"
+ # Style is deprecated, but if it is set, then it trumps method
+ # keyword. TODO: Remove this keyword and this check at some point in
+ # the future.
+ if style is not None:
+ method = style
+ if method == "sum":
+ self.method = "integrate"
self._sum_only = True
else:
- self.proj_style = style
+ self.method = method
self._sum_only = False
- if style == "mip":
+ if self.method == "mip":
self.func = np.max
- elif style == "integrate" or style == "sum":
+ elif self.method == "integrate":
self.func = np.sum # for the future
else:
- raise NotImplementedError(style)
+ raise NotImplementedError(self.method)
self._set_center(center)
if data_source is None: data_source = self.ds.all_data()
for k, v in data_source.field_parameters.items():
@@ -260,7 +269,7 @@
self.ds.domain_left_edge[xax],
self.ds.domain_right_edge[yax])
return QuadTree(np.array([xd,yd], dtype='int64'), nvals,
- bounds, style = self.proj_style)
+ bounds, method = self.method)
def get_data(self, fields = None):
fields = fields or []
@@ -282,10 +291,10 @@
get_memory_usage()/1024.)
self._handle_chunk(chunk, fields, tree)
# Note that this will briefly double RAM usage
- if self.proj_style == "mip":
+ if self.method == "mip":
merge_style = -1
op = "max"
- elif self.proj_style == "integrate":
+ elif self.method == "integrate":
merge_style = 1
op = "sum"
else:
@@ -324,7 +333,10 @@
finfo = self.ds._get_field_info(*field)
mylog.debug("Setting field %s", field)
units = finfo.units
- if self.weight_field is None and not self._sum_only:
+ # add length units to "projected units" if non-weighted
+ # integral projection
+ if self.weight_field is None and not self._sum_only and \
+ self.method == 'integrate':
# See _handle_chunk where we mandate cm
if units == '':
input_units = "cm"
@@ -336,7 +348,9 @@
self[field] = YTArray(field_data[fi].ravel(),
input_units=input_units,
registry=self.ds.unit_registry)
- if self.weight_field is None and not self._sum_only:
+ # convert units if non-weighted integral projection
+ if self.weight_field is None and not self._sum_only and \
+ self.method == 'integrate':
u_obj = Unit(units, registry=self.ds.unit_registry)
if ((u_obj.is_code_unit or self.ds.no_cgs_equiv_length) and
not u_obj.is_dimensionless) and input_units != units:
@@ -355,7 +369,7 @@
tree.initialize_chunk(i1, i2, ilevel)
def _handle_chunk(self, chunk, fields, tree):
- if self.proj_style == "mip" or self._sum_only:
+ if self.method == "mip" or self._sum_only:
dl = 1.0
else:
# This gets explicitly converted to cm
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 yt/data_objects/data_containers.py
--- a/yt/data_objects/data_containers.py
+++ b/yt/data_objects/data_containers.py
@@ -496,7 +496,8 @@
# really ugly check to ensure that this field really does exist somewhere,
# in some naming convention, before returning it as a possible field type
- if (ftype,fname) not in self.ds.field_list and \
+ if (ftype,fname) not in self.ds.field_info and \
+ (ftype,fname) not in self.ds.field_list and \
fname not in self.ds.field_list and \
(ftype,fname) not in self.ds.derived_field_list and \
fname not in self.ds.derived_field_list and \
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 yt/frontends/boxlib/data_structures.py
--- a/yt/frontends/boxlib/data_structures.py
+++ b/yt/frontends/boxlib/data_structures.py
@@ -683,7 +683,7 @@
with open(fn, 'r') as f:
lines = f.readlines()
self.num_stars = int(lines[0].strip()[0])
- for line in lines[1:]:
+ for num, line in enumerate(lines[1:]):
particle_position_x = float(line.split(' ')[1])
particle_position_y = float(line.split(' ')[2])
particle_position_z = float(line.split(' ')[3])
@@ -704,6 +704,12 @@
ind = np.where(self.grids == grid)[0][0]
self.grid_particle_count[ind] += 1
self.grids[ind].NumberOfParticles += 1
+
+ # store the position in the particle file for fast access.
+ try:
+ self.grids[ind]._particle_line_numbers.append(num + 1)
+ except AttributeError:
+ self.grids[ind]._particle_line_numbers = [num + 1]
return True
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 yt/frontends/boxlib/fields.py
--- a/yt/frontends/boxlib/fields.py
+++ b/yt/frontends/boxlib/fields.py
@@ -15,6 +15,7 @@
import numpy as np
import string
+import re
from yt.utilities.physical_constants import \
mh, boltzmann_constant_cgs, amu_cgs
@@ -25,6 +26,9 @@
mom_units = "code_mass / (code_time * code_length**2)"
eden_units = "code_mass / (code_time**2 * code_length)" # erg / cm^3
+spec_finder = re.compile(r'.*\((\D*)(\d*)\).*')
+
+
def _thermal_energy_density(field, data):
# What we've got here is UEINT:
# u here is velocity
@@ -35,18 +39,21 @@
+ data["momentum_z"]**2) / data["density"]
return data["eden"] - ke
+
def _thermal_energy(field, data):
# This is little e, so we take thermal_energy_density and divide by density
return data["thermal_energy_density"] / data["density"]
-def _temperature(field,data):
+
+def _temperature(field, data):
mu = data.ds.parameters["mu"]
gamma = data.ds.parameters["gamma"]
- tr = data["thermal_energy_density"] / data["density"]
+ tr = data["thermal_energy_density"] / data["density"]
tr *= mu * amu_cgs / boltzmann_constant_cgs
tr *= (gamma - 1.0)
return tr
+
class BoxlibFieldInfo(FieldInfoContainer):
known_other_fields = (
("density", (rho_units, ["density"], None)),
@@ -91,11 +98,11 @@
if any(f[1] == "xmom" for f in self.field_list):
self.setup_momentum_to_velocity()
self.add_field(("gas", "thermal_energy"),
- function = _thermal_energy,
- units = "erg/g")
+ function=_thermal_energy,
+ units="erg/g")
self.add_field(("gas", "thermal_energy_density"),
- function = _thermal_energy_density,
- units = "erg/cm**3")
+ function=_thermal_energy_density,
+ units="erg/cm**3")
if ("gas", "temperature") not in self.field_aliases:
self.add_field(("gas", "temperature"),
function=_temperature,
@@ -107,8 +114,9 @@
return data["%smom" % axis]/data["density"]
for ax in 'xyz':
self.add_field(("gas", "velocity_%s" % ax),
- function = _get_vel(ax),
- units = "cm/s")
+ function=_get_vel(ax),
+ units="cm/s")
+
class CastroFieldInfo(FieldInfoContainer):
@@ -125,22 +133,27 @@
# internal energy density (not just thermal)
("rho_e", ("erg/cm**3", [], r"\rho e")),
("Temp", ("K", ["temperature"], r"T")),
- ("grav_x", ("cm/s**2", [], r"g\cdot e_x")),
- ("grav_y", ("cm/s**2", [], r"g\cdot e_y")),
- ("grav_z", ("cm/s**2", [], r"g\cdot e_z")),
+ ("grav_x", ("cm/s**2", [],
+ r"\left(\mathbf{g} \cdot \mathbf{e}\right)_x")),
+ ("grav_y", ("cm/s**2", [],
+ r"\left(\mathbf{g} \cdot \mathbf{e}\right)_y")),
+ ("grav_z", ("cm/s**2", [],
+ r"\left(\mathbf{g} \cdot \mathbf{e}\right)_z")),
("pressure", ("dyne/cm**2", [], r"p")),
- ("kineng", ("erg/cm**3", [], r"\frac{1}{2}\rho|U|**2")),
- ("soundspeed", ("cm/s", ["sound_speed"], None)),
- ("Machnumber", ("", ["mach_number"], None)),
+ ("kineng", ("erg/cm**3", [], r"\frac{1}{2}\rho|\mathbf{U}|**2")),
+ ("soundspeed", ("cm/s", ["sound_speed"], "Sound Speed")),
+ ("Machnumber", ("", ["mach_number"], "Mach Number")),
("entropy", ("erg/(g*K)", ["entropy"], r"s")),
- ("magvort", ("1/s", ["vorticity_magnitude"], r"|\nabla \times U|")),
- ("divu", ("1/s", [], r"\nabla \cdot U")),
+ ("magvort", ("1/s", ["vorticity_magnitude"],
+ r"|\nabla \times \mathbf{U}|")),
+ ("divu", ("1/s", [], r"\nabla \cdot \mathbf{U}")),
("eint_E", ("erg/g", [], r"e(E,U)")),
("eint_e", ("erg/g", [], r"e")),
- ("magvel", ("cm/s", ["velocity_magnitude"], r"|U|")),
- ("radvel", ("cm/s", [], r"U\cdot e_r")),
- ("magmom", ("g*cm/s", ["momentum_magnitude"], r"|\rho U|")),
- ("maggrav", ("cm/s**2", [], r"|g|")),
+ ("magvel", ("cm/s", ["velocity_magnitude"], r"|\mathbf{U}|")),
+ ("radvel", ("cm/s", [],
+ r"\left(\mathbf{U} \cdot \mathbf{e}\right)_r")),
+ ("magmom", ("g*cm/s", ["momentum_magnitude"], r"|\rho \mathbf{U}|")),
+ ("maggrav", ("cm/s**2", [], r"|\mathbf{g}|")),
("phiGrav", ("erg/g", [], r"|\Phi|")),
)
@@ -149,15 +162,12 @@
for _, field in self.ds.field_list:
if field.startswith("X("):
# We have a fraction
- nice_name = field[2:-1]
- self.alias(("gas", "%s_fraction" % nice_name), ("boxlib", field),
- units = "")
- def _create_density_func(field_name):
- def _func(field, data):
- return data[field_name] * data["gas", "density"]
- return _func
+ nice_name, tex_label = _nice_species_name(field)
+ self.alias(("gas", "%s_fraction" % nice_name),
+ ("boxlib", field),
+ units="")
func = _create_density_func(("gas", "%s_fraction" % nice_name))
- self.add_field(name = ("gas", "%s_density" % nice_name),
+ self.add_field(name=("gas", "%s_density" % nice_name),
function = func,
units = "g/cm**3")
# We know this will either have one letter, or two.
@@ -168,6 +178,7 @@
weight = int(weight)
# Here we can, later, add number density.
+
class MaestroFieldInfo(FieldInfoContainer):
known_other_fields = (
@@ -175,32 +186,35 @@
("x_vel", ("cm/s", ["velocity_x"], r"\tilde{u}")),
("y_vel", ("cm/s", ["velocity_y"], r"\tilde{v}")),
("z_vel", ("cm/s", ["velocity_z"], r"\tilde{w}")),
- ("magvel", ("cm/s", ["velocity_magnitude"], r"|\tilde{U} + w_0 e_r|")),
+ ("magvel", ("cm/s", ["velocity_magnitude"],
+ r"|\tilde{\mathbf{U}} + w_0 \mathbf{e}_r|")),
("radial_velocity", ("cm/s", [], r"U\cdot e_r")),
- ("tfromp", ("K", [], None)),
- ("tfromh", ("K", [], None)),
- ("Machnumber", ("", ["mach_number"], None)),
+ ("tfromp", ("K", [], "T(\\rho,p,X)")),
+ ("tfromh", ("K", [], "T(\\rho,h,X)")),
+ ("Machnumber", ("", ["mach_number"], "Mach Number")),
("S", ("1/s", [], None)),
("ad_excess", ("", [], "Adiabatic Excess")),
- ("deltaT", ("", [], None)),
- ("deltagamma", ("", [], None)),
- ("deltap", ("", [], None)),
- ("divw0", ("1/s", [], None)),
+ ("deltaT", ("", [], "[T(\\rho,h,X) - T(\\rho,p,X)]/T(\\rho,h,X)")),
+ ("deltagamma", ("", [], "\Gamma_1 - \overline{\Gamma_1}")),
+ ("deltap", ("", [], "[p(\\rho,h,X) - p_0] / p_0")),
+ ("divw0", ("1/s", [], "\nabla \cdot \mathbf{w}_0")),
# Specific entropy
- ("entropy", ("erg/(g*K)", ["entropy"], None)),
- ("entropypert", ("", [], None)),
- ("enucdot", ("erg/(g*s)", [], None)),
- ("gpi_x", ("dyne/cm**3", [], None)), # Perturbational pressure grad
- ("gpi_y", ("dyne/cm**3", [], None)),
- ("gpi_z", ("dyne/cm**3", [], None)),
- ("h", ("erg/g", [], "Specific Enthalpy")),
- ("h0", ("erg/g", [], "Base State Specific Enthalpy")),
+ ("entropy", ("erg/(g*K)", ["entropy"], "s")),
+ ("entropypert", ("", [], "[s - \overline{s}] / \overline{s}")),
+ ("enucdot", ("erg/(g*s)", [], "\dot{\epsilon_{nuc}}")),
+ ("Hext", ("erg/(g*s)", [], "H_{ext}")),
+ # Perturbational pressure grad
+ ("gpi_x", ("dyne/cm**3", [], "\left(\nabla\pi\right)_x")),
+ ("gpi_y", ("dyne/cm**3", [], "\left(\nabla\pi\right)_y")),
+ ("gpi_z", ("dyne/cm**3", [], "\left(\nabla\pi\right)_z")),
+ ("h", ("erg/g", [], "h")),
+ ("h0", ("erg/g", [], "h_0")),
# Momentum cannot be computed because we need to include base and
# full state.
("momentum", ("g*cm/s", ["momentum_magnitude"], None)),
("p0", ("erg/cm**3", [], "p_0")),
("p0pluspi", ("erg/cm**3", [], "p_0 + \pi")),
- ("pi", ("erg/cm**3", [], None)),
+ ("pi", ("erg/cm**3", [], "\pi")),
("pioverp0", ("", [], "\pi/p_0")),
# Base state density
("rho0", ("g/cm**3", [], "\\rho_0")),
@@ -211,39 +225,42 @@
("rhopert", ("g/cm**3", [], "\\rho^\prime")),
("soundspeed", ("cm/s", ["sound_speed"], None)),
("sponge", ("", [], None)),
- ("tpert", ("K", [], None)),
+ ("tpert", ("K", [], "T - \overline{T}")),
# Again, base state -- so we can't compute ourselves.
- ("vort", ("1/s", ["vorticity_magnitude"], None)),
+ ("vort", ("1/s", ["vorticity_magnitude"], "|\nabla\times\tilde{U}|")),
# Base state
- ("w0_x", ("cm/s", [], None)),
- ("w0_y", ("cm/s", [], None)),
- ("w0_z", ("cm/s", [], None)),
+ ("w0_x", ("cm/s", [], "(w_0)_x")),
+ ("w0_y", ("cm/s", [], "(w_0)_y")),
+ ("w0_z", ("cm/s", [], "(w_0)_z")),
)
def setup_fluid_fields(self):
# pick the correct temperature field
if self.ds.parameters["use_tfromp"]:
self.alias(("gas", "temperature"), ("boxlib", "tfromp"),
- units = "K")
+ units="K")
else:
self.alias(("gas", "temperature"), ("boxlib", "tfromh"),
- units = "K")
+ units="K")
# Add X's and omegadots, units of 1/s
for _, field in self.ds.field_list:
if field.startswith("X("):
- # We have a fraction
- nice_name = field[2:-1]
- self.alias(("gas", "%s_fraction" % nice_name), ("boxlib", field),
- units = "")
- def _create_density_func(field_name):
- def _func(field, data):
- return data[field_name] * data["gas", "density"]
- return _func
+ # We have a mass fraction
+ nice_name, tex_label = _nice_species_name(field)
+ # Overwrite field to use nicer tex_label display_name
+ self.add_output_field(("boxlib", field),
+ units="",
+ display_name=tex_label)
+ self.alias(("gas", "%s_fraction" % nice_name),
+ ("boxlib", field),
+ units="")
func = _create_density_func(("gas", "%s_fraction" % nice_name))
- self.add_field(name = ("gas", "%s_density" % nice_name),
- function = func,
- units = "g/cm**3")
+ self.add_field(name=("gas", "%s_density" % nice_name),
+ function=func,
+ units="g/cm**3",
+ display_name=r'\rho %s' % tex_label)
+
# Most of the time our species will be of the form
# element name + atomic weight (e.g. C12), but
# sometimes we make up descriptive names (e.g. ash)
@@ -256,8 +273,30 @@
weight = int(weight)
# Here we can, later, add number density.
- if field.startswith("omegadot("):
- nice_name = field[9:-1]
- self.add_output_field(("boxlib", field), units = "1/s")
+ elif field.startswith("omegadot("):
+ nice_name, tex_label = _nice_species_name(field)
+ display_name = r'\dot{\omega}\left[%s\right]' % tex_label
+ # Overwrite field to use nicer tex_label'ed display_name
+ self.add_output_field(("boxlib", field), units="1/s",
+ display_name=display_name)
self.alias(("gas", "%s_creation_rate" % nice_name),
- ("boxlib", field), units = "1/s")
+ ("boxlib", field), units="1/s")
+
+
+def _nice_species_name(field):
+ spec_match = spec_finder.search(field)
+ nice_name = ''.join(spec_match.groups())
+ # if the species field is a descriptive name, then the match
+ # on the integer will be blank
+ # modify the tex string in this case to remove spurious tex spacing
+ lab = r"X\left(^{%s}%s\right)"
+ if spec_match.groups()[-1] == "":
+ lab = r"X\left(%s%s\right)"
+ tex_label = lab % spec_match.groups()[::-1]
+ return nice_name, tex_label
+
+
+def _create_density_func(field_name):
+ def _func(field, data):
+ return data[field_name] * data["gas", "density"]
+ return _func
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 yt/frontends/boxlib/io.py
--- a/yt/frontends/boxlib/io.py
+++ b/yt/frontends/boxlib/io.py
@@ -125,30 +125,36 @@
rv[ftype, fname] = np.concatenate((data, rv[ftype, fname]))
return rv
- def _read_particles(self, grid, field):
+ def _read_particles(self, grid, field):
"""
parses the Orion Star Particle text files
"""
- fn = self.particle_filename
+ particles = []
+
+ if grid.NumberOfParticles == 0:
+ return np.array(particles)
def read(line, field):
entry = line.strip().split(' ')[self.particle_field_index[field]]
return np.float(entry)
- with open(fn, 'r') as f:
- lines = f.readlines()
- particles = []
- for line in lines[1:]:
- if grid.NumberOfParticles > 0:
- coord = read(line, "particle_position_x"), \
- read(line, "particle_position_y"), \
- read(line, "particle_position_z")
- if ( (grid.LeftEdge <= coord).all() and
- (coord <= grid.RightEdge).all() ):
- particles.append(read(line, field))
- return np.array(particles)
+ try:
+ lines = self._cached_lines
+ for num in grid._particle_line_numbers:
+ line = lines[num]
+ particles.append(read(line, field))
+ return np.array(particles)
+ except AttributeError:
+ fn = self.particle_filename
+ with open(fn, 'r') as f:
+ lines = f.readlines()
+ self._cached_lines = lines
+ for num in grid._particle_line_numbers:
+ line = lines[num]
+ particles.append(read(line, field))
+ return np.array(particles)
class IOHandlerCastro(IOHandlerBoxlib):
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 yt/frontends/chombo/data_structures.py
--- a/yt/frontends/chombo/data_structures.py
+++ b/yt/frontends/chombo/data_structures.py
@@ -255,18 +255,6 @@
if D == 2:
self.dataset_type = 'chombo2d_hdf5'
- # some datasets will not be time-dependent, and to make
- # matters worse, the simulation time is not always
- # stored in the same place in the hdf file! Make
- # sure we handle that here.
- try:
- self.current_time = self._handle.attrs['time']
- except KeyError:
- try:
- self.current_time = self._handle['/level_0'].attrs['time']
- except KeyError:
- self.current_time = 0.0
-
self.geometry = "cartesian"
self.ini_filename = ini_filename
self.fullplotdir = os.path.abspath(filename)
@@ -315,6 +303,20 @@
self.refine_by = self._handle['/level_0'].attrs['ref_ratio']
self._determine_periodic()
+ self._determine_current_time()
+
+ def _determine_current_time(self):
+ # some datasets will not be time-dependent, and to make
+ # matters worse, the simulation time is not always
+ # stored in the same place in the hdf file! Make
+ # sure we handle that here.
+ try:
+ self.current_time = self._handle.attrs['time']
+ except KeyError:
+ try:
+ self.current_time = self._handle['/level_0'].attrs['time']
+ except KeyError:
+ self.current_time = 0.0
def _determine_periodic(self):
# we default to true unless the HDF5 file says otherwise
@@ -498,6 +500,7 @@
self.domain_right_edge = np.concatenate((self.domain_right_edge, [1.0]))
self.domain_dimensions = np.concatenate((self.domain_dimensions, [1]))
+ self._determine_current_time()
@classmethod
def _is_valid(self, *args, **kwargs):
@@ -540,14 +543,14 @@
with open(self.particle_filename, 'r') as f:
lines = f.readlines()
self.num_stars = int(lines[0].strip().split(' ')[0])
- for line in lines[1:]:
+ for num, line in enumerate(lines[1:]):
particle_position_x = float(line.split(' ')[1])
particle_position_y = float(line.split(' ')[2])
particle_position_z = float(line.split(' ')[3])
coord = [particle_position_x, particle_position_y, particle_position_z]
# for each particle, determine which grids contain it
# copied from object_finding_mixin.py
- mask=np.ones(self.num_grids)
+ mask = np.ones(self.num_grids)
for i in xrange(len(coord)):
np.choose(np.greater(self.grid_left_edge.d[:,i],coord[i]), (mask,0), mask)
np.choose(np.greater(self.grid_right_edge.d[:,i],coord[i]), (0,mask), mask)
@@ -562,6 +565,12 @@
self.grid_particle_count[ind] += 1
self.grids[ind].NumberOfParticles += 1
+ # store the position in the *.sink file for fast access.
+ try:
+ self.grids[ind]._particle_line_numbers.append(num + 1)
+ except AttributeError:
+ self.grids[ind]._particle_line_numbers = [num + 1]
+
class Orion2Dataset(ChomboDataset):
@@ -595,6 +604,7 @@
self.domain_dimensions = self._calc_domain_dimensions()
self.refine_by = self._handle['/level_0'].attrs['ref_ratio']
self._determine_periodic()
+ self._determine_current_time()
def _parse_inputs_file(self, ini_filename):
self.fullplotdir = os.path.abspath(self.parameter_filename)
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 yt/frontends/chombo/io.py
--- a/yt/frontends/chombo/io.py
+++ b/yt/frontends/chombo/io.py
@@ -69,11 +69,11 @@
self._particle_field_index = field_dict
return self._particle_field_index
- def _read_field_names(self,grid):
+ def _read_field_names(self, grid):
ncomp = int(self._handle.attrs['num_components'])
fns = [c[1] for c in f.attrs.items()[-ncomp-1:-1]]
- def _read_data(self,grid,field):
+ def _read_data(self, grid, field):
lstring = 'level_%i' % grid.Level
lev = self._handle[lstring]
dims = grid.ActiveDimensions
@@ -289,20 +289,27 @@
"""
+ particles = []
+
+ if grid.NumberOfParticles == 0:
+ return np.array(particles)
+
def read(line, field):
entry = line.strip().split(' ')[self.particle_field_index[field]]
return np.float(entry)
- fn = grid.ds.fullplotdir[:-4] + "sink"
- with open(fn, 'r') as f:
- lines = f.readlines()
- particles = []
- for line in lines[1:]:
- if grid.NumberOfParticles > 0:
- coord = read(line, "particle_position_x"), \
- read(line, "particle_position_y"), \
- read(line, "particle_position_z")
- if ((grid.LeftEdge <= coord).all() and
- (coord <= grid.RightEdge).all() ):
- particles.append(read(line, field))
- return np.array(particles)
+ try:
+ lines = self._cached_lines
+ for num in grid._particle_line_numbers:
+ line = lines[num]
+ particles.append(read(line, field))
+ return np.array(particles)
+ except AttributeError:
+ fn = grid.ds.fullplotdir[:-4] + "sink"
+ with open(fn, 'r') as f:
+ lines = f.readlines()
+ self._cached_lines = lines
+ for num in grid._particle_line_numbers:
+ line = lines[num]
+ particles.append(read(line, field))
+ return np.array(particles)
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 yt/frontends/gdf/data_structures.py
--- a/yt/frontends/gdf/data_structures.py
+++ b/yt/frontends/gdf/data_structures.py
@@ -26,12 +26,20 @@
GridIndex
from yt.data_objects.static_output import \
Dataset
+from yt.utilities.exceptions import \
+ YTGDFUnknownGeometry
from yt.utilities.lib.misc_utilities import \
get_box_grids_level
-from yt.units.yt_array import \
- uconcatenate, YTArray
+from .fields import GDFFieldInfo
-from .fields import GDFFieldInfo
+
+GEOMETRY_TRANS = {
+ 0: "cartesian",
+ 1: "polar",
+ 2: "cylindrical",
+ 3: "spherical",
+}
+
class GDFGrid(AMRGridPatch):
_id_offset = 0
@@ -65,6 +73,7 @@
self.dds
self.dds = self.ds.arr(self.dds, "code_length")
+
class GDFHierarchy(GridIndex):
grid = GDFGrid
@@ -145,8 +154,9 @@
Gets back all the grids between a left edge and right edge
"""
eps = np.finfo(np.float64).eps
- grid_i = np.where(np.all((self.grid_right_edge - left_edge) > eps, axis=1) &
- np.all((right_edge - self.grid_left_edge) > eps, axis=1))
+ grid_i = np.where(
+ np.all((self.grid_right_edge - left_edge) > eps, axis=1) &
+ np.all((right_edge - self.grid_left_edge) > eps, axis=1))
return self.grids[grid_i], grid_i
@@ -162,7 +172,7 @@
_field_info_class = GDFFieldInfo
def __init__(self, filename, dataset_type='grid_data_format',
- storage_filename=None, geometry = 'cartesian'):
+ storage_filename=None, geometry=None):
self.geometry = geometry
self.fluid_types += ("gdf",)
Dataset.__init__(self, filename, dataset_type)
@@ -198,7 +208,7 @@
current_unit = h5f["/dataset_units/%s" % unit_name]
value = current_unit.value
unit = current_unit.attrs["unit"]
- setattr(self, unit_name, self.quan(value,unit))
+ setattr(self, unit_name, self.quan(value, unit))
else:
self.length_unit = self.quan(1.0, "cm")
self.mass_unit = self.quan(1.0, "g")
@@ -214,6 +224,12 @@
else:
self.data_software = "unknown"
sp = self._handle["/simulation_parameters"].attrs
+ if self.geometry is None:
+ geometry = just_one(sp.get("geometry", 0))
+ try:
+ self.geometry = GEOMETRY_TRANS[geometry]
+ except KeyError:
+ raise YTGDFUnknownGeometry(geometry)
self.parameters.update(sp)
self.domain_left_edge = sp["domain_left_edge"][:]
self.domain_right_edge = sp["domain_right_edge"][:]
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 yt/utilities/exceptions.py
--- a/yt/utilities/exceptions.py
+++ b/yt/utilities/exceptions.py
@@ -94,7 +94,7 @@
def __str__(self):
return ("The display name \"%s\" "
- "of the derived field %s "
+ "of the derived field %s "
"contains the following LaTeX parser errors:\n" ) \
% (self.display_name, self.field_name) + self.mathtext_error
@@ -106,7 +106,7 @@
def __str__(self):
return ("The unit display name \"%s\" "
- "of the derived field %s "
+ "of the derived field %s "
"contains the following LaTeX parser errors:\n" ) \
% (self.unit_name, self.field_name) + self.mathtext_error
@@ -116,7 +116,7 @@
def __str__(self):
return self.message
-
+
class MissingParameter(YTException):
def __init__(self, ds, parameter):
YTException.__init__(self, ds=ds)
@@ -425,3 +425,14 @@
def __str__(self):
return "A file already exists at %s and clobber=False." % self.filename
+
+
+class YTGDFUnknownGeometry(Exception):
+ def __init__(self, geometry):
+ self.geometry = geometry
+
+ def __str__(self):
+ return '''Unknown geometry %i. Please refer to GDF standard
+ for more information''' % self.geometry
+
+
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 yt/utilities/fits_image.py
--- a/yt/utilities/fits_image.py
+++ b/yt/utilities/fits_image.py
@@ -341,7 +341,7 @@
def __init__(self, ds, axis, fields, center="c", weight_field=None, **kwargs):
fields = ensure_list(fields)
axis = fix_axis(axis, ds)
- center = get_sanitized_center(center, ds)
+ center = ds.coordinates.sanitize_center(center, axis)
prj = ds.proj(fields[0], axis, weight_field=weight_field, **kwargs)
w, frb = construct_image(prj, center=center)
super(FITSProjection, self).__init__(frb, fields=fields, wcs=w)
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 yt/utilities/lib/QuadTree.pyx
--- a/yt/utilities/lib/QuadTree.pyx
+++ b/yt/utilities/lib/QuadTree.pyx
@@ -113,10 +113,10 @@
cdef np.int64_t last_dims[2]
def __cinit__(self, np.ndarray[np.int64_t, ndim=1] top_grid_dims,
- int nvals, bounds, style = "integrate"):
- if style == "integrate":
+ int nvals, bounds, method = "integrate"):
+ if method == "integrate":
self.combine = QTN_add_value
- elif style == "mip":
+ elif method == "mip":
self.combine = QTN_max_value
else:
raise NotImplementedError
@@ -212,10 +212,10 @@
def frombuffer(self, np.ndarray[np.int32_t, ndim=1] refined,
np.ndarray[np.float64_t, ndim=2] values,
np.ndarray[np.float64_t, ndim=1] wval,
- style):
- if style == "mip" or style == -1:
+ method):
+ if method == "mip" or method == -1:
self.merged = -1
- elif style == "integrate" or style == 1:
+ elif method == "integrate" or method == 1:
self.merged = 1
cdef int curpos = 0
cdef QuadTreeNode *root
@@ -348,11 +348,11 @@
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
- def get_all(self, int count_only = 0, int style = 1):
+ def get_all(self, int count_only = 0, int method = 1):
cdef int i, j, vi
cdef int total = 0
vals = []
- self.merged = style
+ self.merged = method
for i in range(self.top_grid_dims[0]):
for j in range(self.top_grid_dims[1]):
total += self.count(self.root_nodes[i][j])
@@ -540,14 +540,14 @@
else:
raise RuntimeError
-def merge_quadtrees(QuadTree qt1, QuadTree qt2, style = 1):
+def merge_quadtrees(QuadTree qt1, QuadTree qt2, method = 1):
cdef int i, j
qt1.num_cells = 0
cdef QTN_combine *func
- if style == 1:
+ if method == 1:
qt1.merged = 1
func = QTN_add_value
- elif style == -1:
+ elif method == -1:
qt1.merged = -1
func = QTN_max_value
else:
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 yt/visualization/base_plot_types.py
--- a/yt/visualization/base_plot_types.py
+++ b/yt/visualization/base_plot_types.py
@@ -121,8 +121,8 @@
bmap = brewer2mpl.get_map(*cmap)
cmap = bmap.get_mpl_colormap(N=cmap[2])
else:
- raise RuntimeError("Please install brewer2mpl to use colorbrewer colormaps")
-
+ raise RuntimeError(
+ "Please install brewer2mpl to use colorbrewer colormaps")
self.image = self.axes.imshow(data.to_ndarray(), origin='lower',
extent=extent, norm=norm, vmin=self.zmin,
aspect=aspect, vmax=self.zmax, cmap=cmap)
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 yt/visualization/eps_writer.py
--- a/yt/visualization/eps_writer.py
+++ b/yt/visualization/eps_writer.py
@@ -262,7 +262,7 @@
Parameters
----------
- plot : `yt.visualization.plot_types.RavenPlot`
+ plot : `yt.visalization.plot_window.PlotWindow`
yt plot on which the axes are based.
units : string
Unit description that overrides yt's unit description. Only
@@ -422,7 +422,7 @@
Parameters
----------
- plot : `yt.visualization.plot_types.VMPlot`
+ plot : `yt.visalization.plot_window.PlotWindow`
yt plot that provides the image
pos : tuple of floats
Position of the origin of the image in centimeters.
@@ -446,13 +446,6 @@
shift = 0.0
if self.canvas is None:
self.canvas = pyx.canvas.canvas()
- if isinstance(plot, VMPlot):
- if plot.colorbar != None:
- mylog.warning("Image (slices, projections, etc.) plots must not"\
- "have a colorbar. Removing it.")
- plot.colorbar = None
- plot._redraw_image()
- _p1 = plot._figure
elif isinstance(plot, (PlotWindow, PhasePlot)):
self.field = field
if self.field == None:
@@ -627,7 +620,7 @@
Parameters
----------
- plot : `yt.visualization.plot_types.VMPlot`
+ plot : A yt plot
yt plot from which the information is taken.
Examples
@@ -649,14 +642,7 @@
_cmap = plot.cmap.name
if _cmap == None:
_cmap = 'algae'
- if isinstance(plot, VMPlot):
- proj = "Proj" in plot._type_name and \
- plot.data._weight is None
- _zlabel = plot.ds.field_info[plot.axis_names["Z"]].get_label(proj)
- _zlabel = _zlabel.replace("_","\;")
- _zlog = plot.log_field
- _zrange = (plot.norm.vmin, plot.norm.vmax)
- elif isinstance(plot, (PlotWindow, PhasePlot)):
+ if isinstance(plot, (PlotWindow, PhasePlot)):
proj = plot._plot_type.endswith("Projection") and \
plot.data_source.weight_field == None
if isinstance(plot, PlotWindow):
@@ -905,7 +891,7 @@
shrink_cb=0.95, figsize=(8,8), margins=(0,0), titles=None,
savefig=None, format="eps", yt_nocbar=False, bare_axes=False,
xaxis_flags=None, yaxis_flags=None,
- cb_flags=None, cb_location=None, plot_collection=False):
+ cb_flags=None, cb_location=None):
r"""Convenience routine to create a multi-panel figure from yt plots or
JPEGs. The images are first placed from the origin, and then
bottom-to-top and left-to-right.
@@ -916,7 +902,7 @@
Number of columns in the figure.
nrow : integer
Number of rows in the figure.
- yt_plots : list of `yt.visualization.plot_types.VMPlot`
+ yt_plots : list of yt plot instances
yt plots to include in the figure.
images : list of strings
JPEG filenames to include in the figure.
@@ -953,8 +939,6 @@
cb_location : list of strings
Strings to control the location of the colorbar (left, right,
top, bottom)
- plot_collection : boolean
- Set to true to yt_plots is a PlotCollection
Examples
--------
@@ -1153,7 +1137,7 @@
#=============================================================================
def multiplot_yt(ncol, nrow, plots, fields=None, **kwargs):
- r"""Wrapper for multiplot that takes a yt PlotWindow or PlotCollection.
+ r"""Wrapper for multiplot that takes a yt PlotWindow
Accepts all parameters used in multiplot.
@@ -1163,8 +1147,8 @@
Number of columns in the figure.
nrow : integer
Number of rows in the figure.
- plots : `PlotCollection` or `PlotWindow`
- yt PlotCollection or PlotWindow that has the plots to be used.
+ plots : `yt.visualization.plot_window.PlotWindow`
+ yt PlotWindow that has the plots to be used.
Examples
--------
@@ -1183,17 +1167,7 @@
>>> mp = multiplot_yt(2,2,pc,savefig="yt",shrink_cb=0.9, bare_axes=False,
>>> yt_nocbar=False, margins=(0.5,0.5))
"""
- # Determine whether the plots are organized in a PlotCollection,
- # PlotWindow, or list of PlotWindows
- if isinstance(plots, PlotCollection):
- if len(plots.plots) < nrow*ncol:
- raise RuntimeError("Number of plots in PlotCollection is less "\
- "than nrow(%d) x ncol(%d)." % \
- (len(plots.plots), nrow, ncol))
- return
- figure = multiplot(ncol, nrow, yt_plots=plots.plots,
- plot_collection=True, **kwargs)
- elif isinstance(plots, PlotWindow):
+ if isinstance(plots, PlotWindow):
if fields == None:
fields = plots.fields
if len(fields) < nrow*ncol:
@@ -1224,7 +1198,7 @@
Parameters
----------
- plot : `yt.visualization.plot_types.VMPlot`
+ plot : `yt.visalization.plot_window.PlotWindow`
yt plot that provides the image and metadata
figsize : tuple of floats
Size of the figure in centimeters.
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -697,8 +697,8 @@
# This will likely be replaced at some point by the coordinate handler
# setting plot aspect.
if self.aspect is None:
- self.aspect = np.float64(self.ds.quan(1.0, unit_y) /
- self.ds.quan(1.0, unit_x))
+ self.aspect = np.float64((self.ds.quan(1.0, unit_y) /
+ self.ds.quan(1.0, unit_x)).in_cgs())
extentx = [(self.xlim[i] - xc).in_units(unit_x) for i in (0, 1)]
extenty = [(self.ylim[i] - yc).in_units(unit_y) for i in (0, 1)]
@@ -1109,6 +1109,27 @@
The maximum level to project to.
fontsize : integer
The size of the fonts for the axis, colorbar, and tick labels.
+ method : string
+ The method of projection. Valid methods are:
+
+ "integrate" with no weight_field specified : integrate the requested
+ field along the line of sight.
+
+ "integrate" with a weight_field specified : weight the requested
+ field by the weighting field and integrate along the line of sight.
+
+ "mip" : pick out the maximum value of the field in the line of sight.
+
+ "sum" : This method is the same as integrate, except that it does not
+ multiply by a path length when performing the integration, and is
+ just a straight summation of the field along the given axis.
+ proj_style : string
+ The method of projection--same as method keyword. Deprecated as of
+ version 3.0.2. Please use method instead.
+ window_size : float
+ The size of the window in inches. Set to 8 by default.
+ aspect : float
+ The aspect ratio of the plot. Set to None for 1.
field_parameters : dictionary
A dictionary of field parameters than can be accessed by derived
fields.
@@ -1130,20 +1151,25 @@
def __init__(self, ds, axis, fields, center='c', width=None, axes_unit=None,
weight_field=None, max_level=None, origin='center-window',
fontsize=18, field_parameters=None, data_source=None,
- proj_style = "integrate", window_size=8.0, aspect=None):
+ method = "integrate", proj_style = None, window_size=8.0,
+ aspect=None):
ts = self._initialize_dataset(ds)
self.ts = ts
ds = self.ds = ts[0]
axis = fix_axis(axis, ds)
+ # proj_style is deprecated, but if someone specifies then it trumps
+ # method.
+ if proj_style is not None:
+ method = proj_style
# If a non-weighted integral projection, assure field-label reflects that
- if weight_field is None and proj_style == "integrate":
+ if weight_field is None and method == "integrate":
self.projected = True
(bounds, center, display_center) = \
get_window_parameters(axis, center, width, ds)
if field_parameters is None: field_parameters = {}
proj = ds.proj(fields, axis, weight_field=weight_field,
center=center, data_source=data_source,
- field_parameters = field_parameters, style = proj_style)
+ field_parameters = field_parameters, method = method)
PWViewerMPL.__init__(self, proj, bounds, fields=fields, origin=origin,
fontsize=fontsize, window_size=window_size,
aspect=aspect)
@@ -1236,7 +1262,7 @@
class OffAxisProjectionDummyDataSource(object):
_type_name = 'proj'
- proj_style = 'integrate'
+ method = 'integrate'
_key_fields = []
def __init__(self, center, ds, normal_vector, width, fields,
interpolated, resolution = (800,800), weight=None,
@@ -1348,7 +1374,7 @@
weight=weight_field, volume=volume, no_ghost=no_ghost,
le=le, re=re, north_vector=north_vector)
# If a non-weighted, integral projection, assure field-label reflects that
- if weight_field is None and OffAxisProj.proj_style == "integrate":
+ if weight_field is None and OffAxisProj.method == "integrate":
self.projected = True
# Hard-coding the origin keyword since the other two options
# aren't well-defined for off-axis data objects
@@ -1542,7 +1568,7 @@
if source._type_name in ("slice", "cutting"):
units = finfo.get_units()
elif source._type_name == "proj":
- if source.weight_field is not None or source.proj_style in ("mip", "sum"):
+ if source.weight_field is not None or source.method in ("mip", "sum"):
units = finfo.get_units()
else:
units = finfo.get_projected_units()
@@ -1635,7 +1661,7 @@
self._cb_size = 0.0375*fsize
self._ax_text_size = [1.2*fontscale, 0.9*fontscale]
self._top_buff_size = 0.30*fontscale
- self._aspect = ((extent[1] - extent[0])/(extent[3] - extent[2]))
+ self._aspect = ((extent[1] - extent[0])/(extent[3] - extent[2])).in_cgs()
size, axrect, caxrect = self._get_best_layout()
diff -r 24665f82c66eb316e3d4af2c9938b0b522aabc94 -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 yt/visualization/tests/test_plotwindow.py
--- a/yt/visualization/tests/test_plotwindow.py
+++ b/yt/visualization/tests/test_plotwindow.py
@@ -146,6 +146,12 @@
('gas', 'density'),
)
+PROJECTION_METHODS = (
+ 'integrate',
+ 'sum',
+ 'mip'
+)
+
@requires_ds(M7)
def test_attributes():
"""Test plot member functions that aren't callbacks"""
@@ -240,6 +246,7 @@
projections_c = []
projections_wf = []
projections_w = {}
+ projections_m = []
for dim in range(3):
projections.append(ProjectionPlot(test_ds, dim, "density"))
projections_ds.append(ProjectionPlot(test_ds, dim, "density",
@@ -253,6 +260,9 @@
for wf in WEIGHT_FIELDS:
projections_wf.append(ProjectionPlot(test_ds, dim, "density",
weight_field=wf))
+ for m in PROJECTION_METHODS:
+ projections_m.append(ProjectionPlot(test_ds, dim, "density",
+ method=m))
cls.slices = [SlicePlot(test_ds, dim, "density") for dim in range(3)]
cls.projections = projections
@@ -260,6 +270,7 @@
cls.projections_c = projections_c
cls.projections_wf = projections_wf
cls.projections_w = projections_w
+ cls.projections_m = projections_m
cls.offaxis_slice = OffAxisSlicePlot(test_ds, normal, "density")
cls.offaxis_proj = OffAxisProjectionPlot(test_ds, normal, "density")
@@ -296,6 +307,10 @@
def test_projection_plot_wf(self, dim):
self.projections_wf[dim].save()
+ @parameterized.expand([(i, ) for i in range(len(PROJECTION_METHODS))])
+ def test_projection_plot_m(self, dim):
+ self.projections_m[dim].save()
+
@parameterized.expand(
param.explicit((fname, ))
for fname in TEST_FLNMS)
https://bitbucket.org/yt_analysis/yt/commits/503b3b3f28e5/
Changeset: 503b3b3f28e5
Branch: yt
User: jzuhone
Date: 2014-10-07 19:12:47+00:00
Summary: Fixing thermal broadening and cleaning this up a bit
Affected #: 1 file
diff -r 7181ddfcb573e2c12e0655e94e981ae7651ab4f8 -r 503b3b3f28e5828703ebf4f8011ac509afeda39b yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -27,15 +27,15 @@
return -vz
return _v_los
-fits_info = {"velocity":("m/s","VELOCITY"),
- "frequency":("Hz","FREQUENCY"),
- "energy":("eV","ENERGY"),
- "wavelength":("angstrom","WAVELENG")}
+fits_info = {"velocity":("m/s","VELOCITY","v"),
+ "frequency":("Hz","FREQUENCY","f"),
+ "energy":("eV","ENERGY","E"),
+ "wavelength":("angstrom","WAVELENG","lambda")}
class PPVCube(object):
def __init__(self, ds, normal, field, width=(1.0,"unitary"),
dims=(100,100,100), velocity_bounds=None, rest_value=None,
- ion_weight=None):
+ thermal_broad=False, particle_weight=56.):
r""" Initialize a PPVCube object.
Parameters
@@ -60,9 +60,9 @@
axis of the PPV cube. The spectral axis will be converted to the units and
displaced by the value. Can be in units of energy, wavelength, or frequency.
If not set the default is to leave the spectral axis in velocity units.
- ion_weight : float, optional
- Set this value to the atomic weight of the ion that is emitting the line
- in order to include thermal broadening.
+ particle_weight : float, optional
+ Set this value to the atomic weight of the particle that is emitting the line
+ if *thermal_broad* is True.
Examples
--------
@@ -75,6 +75,8 @@
self.field = field
self.width = width
self.rest_value = rest_value
+ self.particle_mass = particle_weight*mh
+ self.thermal_broad = thermal_broad
self.nx = dims[0]
self.ny = dims[1]
@@ -105,16 +107,16 @@
self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
self.dv = self.vbins[1]-self.vbins[0]
- if ion_weight is None:
- self.ion_mass = mh
- self.phi_th = lambda v, v_th: 1.0
- else:
- self.ion_mass = ion_weight*mh
- self.phi_th = lambda v, v_th: self.dv/(np.sqrt(np.pi)*v_th)*np.exp(-(v/v_th)**2)
-
_vlos = create_vlos(orient.unit_vectors[2])
self.ds.field_info.add_field(("gas","v_los"), function=_vlos, units="cm/s")
+ if thermal_broad:
+ self.v_th = lambda T: np.sqrt(2.*kboltz*T/self.particle_mass)
+ self.phi_th = lambda v, T: self.dv*np.exp(-(v/self.v_th(T))**2)/(np.sqrt(np.pi)*self.v_th(T))
+ else:
+ self.v_th = lambda T: 1.0
+ self.phi_th = lambda v, T: np.maximum(1.-np.abs(v-self.vmid[i])/self.dv.in_units(v.units),0.0)
+
self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.field_units)
pbar = get_pbar("Generating cube.", self.nv)
for i in xrange(self.nv):
@@ -133,7 +135,18 @@
self.rest_value = self.ds.quan(rest_value[0], rest_value[1])
self.vbins = self.rest_value*(1.-self.vbins.in_cgs()/clight)
self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
- self.dv = self.v_bins[1]-self.v_bins[0]
+ self.dv = self.vbins[1]-self.vbins[0]
+
+ dims = self.dv.units.dimensions
+
+ if dims == ytdims.rate:
+ self.axis_type = "frequency"
+ elif dims == ytdims.length:
+ self.axis_type = "wavelength"
+ elif dims == ytdims.energy:
+ self.axis_type = "energy"
+ elif dims == ytdims.velocity:
+ self.axis_type = "velocity"
def write_fits(self, filename, clobber=True, length_unit=(10.0, "kpc"),
sky_center=(30.,45.)):
@@ -163,21 +176,10 @@
center = [0.0,0.0]
types = ["LINEAR","LINEAR"]
- dims = self.dv.units.dimensions
+ vunit = fits_info[self.axis_type][0]
+ vtype = fits_info[self.axis_type][1]
- if dims == ytdims.rate:
- axis_type = "frequency"
- elif dims == ytdims.length:
- axis_type = "wavelength"
- elif dims == ytdims.energy:
- axis_type = "energy"
- elif dims == ytdims.velocity:
- axis_type = "velocity"
-
- vunit = fits_info[axis_type]
- vtype = fits_info[axis_type]
-
- v_center = 0.5*(self.v_bins[0]+self.v_bins[-1]).in_units(vunit).value
+ v_center = 0.5*(self.vbins[0]+self.vbins[-1]).in_units(vunit).value
dx = length_unit[0]/self.nx
dy = length_unit[0]/self.ny
@@ -201,10 +203,15 @@
def _create_intensity(self, i):
def _intensity(field, data):
- vlos = data["v_los"]
- v_th = np.sqrt(2*kboltz*data["temperature"]/self.ion_mass)
- w = np.abs(vlos-self.vmid[i])/self.dv.in_units(vlos.units)
- w = 1.-w
- w[w < 0.0] = 0.0
- return data[self.field]*self.phi_th(vlos, v_th)*w
+ w = self.phi_th(self.vmid[i].in_cgs()-data["v_los"], data["temperature"])
+ return data[self.field]*w
return _intensity
+
+ def __repr__(self):
+ return "PPVCube [%d %d %d] (%s < %s < %s)" % (self.nx, self.ny, self.nv,
+ self.vbins[0],
+ fits_info[self.axis_type][2],
+ self.vbins[-1])
+
+ def __getitem__(self, item):
+ return self.data[item]
https://bitbucket.org/yt_analysis/yt/commits/00b133a4ce33/
Changeset: 00b133a4ce33
Branch: yt
User: jzuhone
Date: 2014-10-07 19:36:58+00:00
Summary: Adding special functions to the on-demand scipy import
Affected #: 1 file
diff -r 503b3b3f28e5828703ebf4f8011ac509afeda39b -r 00b133a4ce333c56525bd24904fc1dba42d36475 yt/utilities/on_demand_imports.py
--- a/yt/utilities/on_demand_imports.py
+++ b/yt/utilities/on_demand_imports.py
@@ -132,4 +132,15 @@
self._interpolate = interpolate
return self._interpolate
+ _special = None
+ @property
+ def special(self):
+ if self._special is None:
+ try:
+ import scipy.special as special
+ except ImportError:
+ special = NotAModule(self._name)
+ self._special = special
+ return self._special
+
_scipy = scipy_imports()
\ No newline at end of file
https://bitbucket.org/yt_analysis/yt/commits/ad1fa6a03255/
Changeset: ad1fa6a03255
Branch: yt
User: jzuhone
Date: 2014-10-07 19:37:35+00:00
Summary: Bug fix, simplifying things a bit
Affected #: 1 file
diff -r 00b133a4ce333c56525bd24904fc1dba42d36475 -r ad1fa6a03255a214fa50991222232844612427d4 yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -62,7 +62,7 @@
If not set the default is to leave the spectral axis in velocity units.
particle_weight : float, optional
Set this value to the atomic weight of the particle that is emitting the line
- if *thermal_broad* is True.
+ if *thermal_broad* is True. Defaults to 56 (Fe).
Examples
--------
@@ -71,6 +71,7 @@
>>> cube = PPVCube(ds, L, "density", width=(10.,"kpc"),
... velocity_bounds=(-5.,4.,"km/s"))
"""
+
self.ds = ds
self.field = field
self.width = width
@@ -115,7 +116,7 @@
self.phi_th = lambda v, T: self.dv*np.exp(-(v/self.v_th(T))**2)/(np.sqrt(np.pi)*self.v_th(T))
else:
self.v_th = lambda T: 1.0
- self.phi_th = lambda v, T: np.maximum(1.-np.abs(v-self.vmid[i])/self.dv.in_units(v.units),0.0)
+ self.phi_th = lambda v, T: np.maximum(1.-np.abs(v)/self.dv.in_units(v.units),0.0)
self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.field_units)
pbar = get_pbar("Generating cube.", self.nv)
@@ -203,7 +204,7 @@
def _create_intensity(self, i):
def _intensity(field, data):
- w = self.phi_th(self.vmid[i].in_cgs()-data["v_los"], data["temperature"])
+ w = self.phi_th((self.vmid[i]-data["v_los"]).in_cgs(), data["temperature"])
return data[self.field]*w
return _intensity
https://bitbucket.org/yt_analysis/yt/commits/544aa453d825/
Changeset: 544aa453d825
Branch: yt
User: jzuhone
Date: 2014-10-08 02:00:55+00:00
Summary: Making some improvements to the writing of FITS files.
Affected #: 1 file
diff -r ad1fa6a03255a214fa50991222232844612427d4 -r 544aa453d82535e53324331d869239df057b80cb yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -18,6 +18,8 @@
from yt.funcs import get_pbar
from yt.utilities.physical_constants import clight, mh, kboltz
import yt.units.dimensions as ytdims
+from yt.units.yt_array import YTQuantity
+from yt.funcs import iterable
def create_vlos(z_hat):
def _v_los(field, data):
@@ -55,8 +57,8 @@
A 3-tuple of (vmin, vmax, units) for the velocity bounds to
integrate over. If None, the largest velocity of the
dataset will be used, e.g. velocity_bounds = (-v.max(), v.max())
- rest_value : tuple, optional
- A (value, unit) tuple indicating the rest value of the spectral
+ rest_value : tuple or YTQuantity, optional
+ A (value, unit) tuple or YTQuantity indicating the rest value of the spectral
axis of the PPV cube. The spectral axis will be converted to the units and
displaced by the value. Can be in units of energy, wavelength, or frequency.
If not set the default is to leave the spectral axis in velocity units.
@@ -75,7 +77,10 @@
self.ds = ds
self.field = field
self.width = width
- self.rest_value = rest_value
+ if isinstance(rest_value, YTQuantity):
+ self.rest_value = rest_value
+ else:
+ self.rest_value = ds.quan(rest_value[0], rest_value[1])
self.particle_mass = particle_weight*mh
self.thermal_broad = thermal_broad
@@ -128,12 +133,10 @@
self.data[:,:,i] = prj[:,:]
ds.field_info.pop(("gas","intensity"))
pbar.update(i)
-
pbar.finish()
if self.rest_value is not None:
# If we want units other than velocity, we re-calculate these quantities
- self.rest_value = self.ds.quan(rest_value[0], rest_value[1])
self.vbins = self.rest_value*(1.-self.vbins.in_cgs()/clight)
self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
self.dv = self.vbins[1]-self.vbins[0]
@@ -149,7 +152,13 @@
elif dims == ytdims.velocity:
self.axis_type = "velocity"
- def write_fits(self, filename, clobber=True, length_unit=(10.0, "kpc"),
+ # Now fix the width
+ if iterable(self.width):
+ self.width = ds.quan(self.width[0], self.width[1])
+ else:
+ self.width = ds.quan(self.width, "code_length")
+
+ def write_fits(self, filename, clobber=True, sky_scale=None,
sky_center=(30.,45.)):
r""" Write the PPVCube to a FITS file.
@@ -159,41 +168,52 @@
The name of the file to write.
clobber : boolean
Whether or not to clobber an existing file with the same name.
- length_unit : tuple, optional
- The length that corresponds to the width of the projection in
- (value, unit) form. Accepts a length unit or 'deg'.
+ sky_scale : tuple or YTQuantity
+ Conversion between an angle unit and a length unit, if sky
+ coordinates are desired.
+ Examples: (1.0, "arcsec/kpc"), YTQuantity(0.001, "deg/kpc")
sky_center : tuple, optional
The (RA, Dec) coordinate in degrees of the central pixel if
- *length_unit* is 'deg'.
+ *sky_scale* has been specified.
Examples
--------
- >>> cube.write_fits("my_cube.fits", clobber=False, length_unit=(5,"deg"))
+ >>> cube.write_fits("my_cube.fits", clobber=False, sky_scale=(1.0,"arcsec/kpc"))
"""
- if length_unit[1] == "deg":
- center = sky_center
- types = ["RA---SIN","DEC--SIN"]
+ if sky_scale is None:
+ center = (0.0,0.0)
+ types = ["LINEAR","LINEAR"]
else:
- center = [0.0,0.0]
- types = ["LINEAR","LINEAR"]
+ if iterable(sky_scale):
+ sky_scale = self.ds.quan(sky_scale[0], sky_scale[1])
+ if sky_center is None:
+ center = (30.,45.)
+ else:
+ center = sky_center
+ types = ["RA---TAN","DEC--TAN"]
vunit = fits_info[self.axis_type][0]
vtype = fits_info[self.axis_type][1]
v_center = 0.5*(self.vbins[0]+self.vbins[-1]).in_units(vunit).value
- dx = length_unit[0]/self.nx
- dy = length_unit[0]/self.ny
- dv = self.dv.in_units(vunit).value
+ if sky_scale:
+ dx = (self.width*sky_scale).in_units("deg")/self.nx
+ units = "deg"
+ else:
+ dx = self.width/self.nx
+ units = str(self.width.units)
+ dy = dx
+ dv = self.dv.in_units(vunit)
- if length_unit[1] == "deg":
+ if sky_scale:
dx *= -1.
w = _astropy.pywcs.WCS(naxis=3)
w.wcs.crpix = [0.5*(self.nx+1), 0.5*(self.ny+1), 0.5*(self.nv+1)]
- w.wcs.cdelt = [dx,dy,dv]
- w.wcs.crval = [center[0], center[1], v_center]
- w.wcs.cunit = [length_unit[1],length_unit[1],vunit]
+ w.wcs.cdelt = [dx.v,dy.v,dv.v]
+ w.wcs.crval = [center[0],center[1],v_center]
+ w.wcs.cunit = [units,units,vunit]
w.wcs.ctype = [types[0],types[1],vtype]
fib = FITSImageBuffer(self.data.transpose(), fields=self.field, wcs=w)
https://bitbucket.org/yt_analysis/yt/commits/54366a025b64/
Changeset: 54366a025b64
Branch: yt
User: jzuhone
Date: 2014-10-08 02:07:45+00:00
Summary: Applying the lessons learned from PPVCube here.
Affected #: 2 files
diff -r 544aa453d82535e53324331d869239df057b80cb -r 54366a025b64acf856e3caac3a923940ee05796f yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -159,7 +159,7 @@
self.width = ds.quan(self.width, "code_length")
def write_fits(self, filename, clobber=True, sky_scale=None,
- sky_center=(30.,45.)):
+ sky_center=None):
r""" Write the PPVCube to a FITS file.
Parameters
diff -r 544aa453d82535e53324331d869239df057b80cb -r 54366a025b64acf856e3caac3a923940ee05796f yt/analysis_modules/sunyaev_zeldovich/projection.py
--- a/yt/analysis_modules/sunyaev_zeldovich/projection.py
+++ b/yt/analysis_modules/sunyaev_zeldovich/projection.py
@@ -279,8 +279,7 @@
self.data["TeSZ"] = self.ds.arr(Te, "keV")
@parallel_root_only
- def write_fits(self, filename, units="kpc", sky_center=None, sky_scale=None,
- time_units="Gyr", clobber=True):
+ def write_fits(self, filename, sky_scale=None, sky_center=None, clobber=True):
r""" Export images to a FITS file. Writes the SZ distortion in all
specified frequencies as well as the mass-weighted temperature and the
optical depth. Distance units are in kpc, unless *sky_center*
@@ -290,12 +289,13 @@
----------
filename : string
The name of the FITS file to be written.
- sky_center : tuple of floats, optional
- The center of the observation in (RA, Dec) in degrees. Only used if
- converting to sky coordinates.
- sky_scale : float, optional
- Scale between degrees and kpc. Only used if
- converting to sky coordinates.
+ sky_scale : tuple or YTQuantity
+ Conversion between an angle unit and a length unit, if sky
+ coordinates are desired.
+ Examples: (1.0, "arcsec/kpc"), YTQuantity(0.001, "deg/kpc")
+ sky_center : tuple, optional
+ The (RA, Dec) coordinate in degrees of the central pixel if
+ *sky_scale* has been specified.
clobber : boolean, optional
If the file already exists, do we overwrite?
@@ -309,21 +309,30 @@
>>> szprj.write_fits("SZbullet.fits", sky_center=sky_center, sky_scale=sky_scale)
"""
- deltas = np.array([self.dx.in_units(units),
- self.dy.in_units(units)])
+ if sky_scale is None:
+ center = (0.0,0.0)
+ else:
+ if iterable(sky_scale):
+ sky_scale = self.ds.quan(sky_scale[0], sky_scale[1])
+ if sky_center is None:
+ center = (30.,45.)
+ else:
+ center = sky_center
- if sky_center is None:
- center = [0.0]*2
- else:
- center = sky_center
+ units = self.ds.get_smallest_appropriate_unit(self.width)
+ # Hack because FITS is stupid and doesn't understand case
+ if units == "Mpc": units = "kpc"
+ dx = self.dx.in_units(units)
+ if sky_scale:
+ dx = (dx*sky_scale).in_units("deg")
units = "deg"
- deltas *= sky_scale
- deltas[0] *= -1.
+ dy = dx
+ if sky_scale:
+ dx *= -1.
from yt.utilities.fits_image import FITSImageBuffer
fib = FITSImageBuffer(self.data, fields=self.data.keys(),
- center=center, units=units,
- scale=deltas)
+ center=center, units=units, scale=[dx.v,dy.v])
fib.update_all_headers("Time", float(self.ds.current_time.in_units(time_units).value))
fib.writeto(filename, clobber=clobber)
https://bitbucket.org/yt_analysis/yt/commits/ca5e04b25ae1/
Changeset: ca5e04b25ae1
Branch: yt
User: jzuhone
Date: 2014-10-08 16:02:42+00:00
Summary: Only want the root process to write the FITS file
Affected #: 1 file
diff -r 54366a025b64acf856e3caac3a923940ee05796f -r ca5e04b25ae1a1e86452d0c363c91a84d76ebbf1 yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -20,6 +20,8 @@
import yt.units.dimensions as ytdims
from yt.units.yt_array import YTQuantity
from yt.funcs import iterable
+from yt.utilities.parallel_tools.parallel_analysis_interface import \
+ parallel_root_only
def create_vlos(z_hat):
def _v_los(field, data):
@@ -158,6 +160,7 @@
else:
self.width = ds.quan(self.width, "code_length")
+ @parallel_root_only
def write_fits(self, filename, clobber=True, sky_scale=None,
sky_center=None):
r""" Write the PPVCube to a FITS file.
https://bitbucket.org/yt_analysis/yt/commits/8e6040366a3e/
Changeset: 8e6040366a3e
Branch: yt
User: jzuhone
Date: 2014-10-08 20:41:29+00:00
Summary: Adding a center keyword, fixing units
Affected #: 1 file
diff -r ca5e04b25ae1a1e86452d0c363c91a84d76ebbf1 -r 8e6040366a3e417b02899ddcefd92cc047515c75 yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -37,7 +37,7 @@
"wavelength":("angstrom","WAVELENG","lambda")}
class PPVCube(object):
- def __init__(self, ds, normal, field, width=(1.0,"unitary"),
+ def __init__(self, ds, normal, field, center="c", width=(1.0,"unitary"),
dims=(100,100,100), velocity_bounds=None, rest_value=None,
thermal_broad=False, particle_weight=56.):
r""" Initialize a PPVCube object.
@@ -50,6 +50,8 @@
The normal vector along with to make the projections.
field : string
The field to project.
+ center : float, tuple, or string
+
width : float or tuple, optional
The width of the projection in length units. Specify a float
for code_length units or a tuple (value, units).
@@ -86,6 +88,8 @@
self.particle_mass = particle_weight*mh
self.thermal_broad = thermal_broad
+ self.center = ds.coordinates.sanitize_center(center, normal)
+
self.nx = dims[0]
self.ny = dims[1]
self.nv = dims[2]
@@ -130,7 +134,7 @@
for i in xrange(self.nv):
_intensity = self._create_intensity(i)
ds.add_field(("gas","intensity"), function=_intensity, units=self.field_units)
- prj = off_axis_projection(ds, ds.domain_center, normal, width,
+ prj = off_axis_projection(ds, self.center, normal, width,
(self.nx, self.ny), "intensity")
self.data[:,:,i] = prj[:,:]
ds.field_info.pop(("gas","intensity"))
@@ -206,6 +210,8 @@
else:
dx = self.width/self.nx
units = str(self.width.units)
+ # Hack because FITS is stupid and doesn't understand case
+ if units == "Mpc": units = "kpc"
dy = dx
dv = self.dv.in_units(vunit)
https://bitbucket.org/yt_analysis/yt/commits/7b3da18a262a/
Changeset: 7b3da18a262a
Branch: yt
User: jzuhone
Date: 2014-10-08 20:45:49+00:00
Summary: Fix
Affected #: 1 file
diff -r 8e6040366a3e417b02899ddcefd92cc047515c75 -r 7b3da18a262a2b973b55972beabf8c087d1ae8bd yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -81,7 +81,7 @@
self.ds = ds
self.field = field
self.width = width
- if isinstance(rest_value, YTQuantity):
+ if isinstance(rest_value, YTQuantity) or rest_value is None:
self.rest_value = rest_value
else:
self.rest_value = ds.quan(rest_value[0], rest_value[1])
https://bitbucket.org/yt_analysis/yt/commits/56e41015df45/
Changeset: 56e41015df45
Branch: yt
User: jzuhone
Date: 2014-10-08 21:03:00+00:00
Summary: Bugfix
Affected #: 1 file
diff -r 7b3da18a262a2b973b55972beabf8c087d1ae8bd -r 56e41015df452bd77d28204c5ce75c8f41ba2afa yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -88,7 +88,7 @@
self.particle_mass = particle_weight*mh
self.thermal_broad = thermal_broad
- self.center = ds.coordinates.sanitize_center(center, normal)
+ self.center = ds.coordinates.sanitize_center(center, normal)[0]
self.nx = dims[0]
self.ny = dims[1]
https://bitbucket.org/yt_analysis/yt/commits/9c9767f99106/
Changeset: 9c9767f99106
Branch: yt
User: jzuhone
Date: 2014-10-09 13:51:43+00:00
Summary: Use ds.proj for on-axis projections
Affected #: 1 file
diff -r 56e41015df452bd77d28204c5ce75c8f41ba2afa -r 9c9767f99106f184a0e6bec615359bb6c87fe48e yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -46,8 +46,10 @@
----------
ds : dataset
The dataset.
- normal : array_like
- The normal vector along with to make the projections.
+ normal : array_like or string
+ The normal vector along with to make the projections. If an array, it
+ will be normalized. If a string, it will be assumed to be along one of the
+ principal axes of the domain ("x","y", or "z").
field : string
The field to project.
center : float, tuple, or string
@@ -94,13 +96,18 @@
self.ny = dims[1]
self.nv = dims[2]
- normal = np.array(normal)
- normal /= np.sqrt(np.dot(normal, normal))
- vecs = np.identity(3)
- t = np.cross(normal, vecs).sum(axis=1)
- ax = t.argmax()
- north = np.cross(normal, vecs[ax,:]).ravel()
- orient = Orientation(normal, north_vector=north)
+ if isinstance(normal, basestring):
+ los_vec = np.zeros(3)
+ los_vec[ds.coordinates.axis_id[normal]] = 1.0
+ else:
+ normal = np.array(normal)
+ normal /= np.sqrt(np.dot(normal, normal))
+ vecs = np.identity(3)
+ t = np.cross(normal, vecs).sum(axis=1)
+ ax = t.argmax()
+ north = np.cross(normal, vecs[ax,:]).ravel()
+ orient = Orientation(normal, north_vector=north)
+ los_vec = orient.unit_vectors[2]
dd = ds.all_data()
@@ -119,7 +126,7 @@
self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
self.dv = self.vbins[1]-self.vbins[0]
- _vlos = create_vlos(orient.unit_vectors[2])
+ _vlos = create_vlos(los_vec)
self.ds.field_info.add_field(("gas","v_los"), function=_vlos, units="cm/s")
if thermal_broad:
@@ -134,9 +141,13 @@
for i in xrange(self.nv):
_intensity = self._create_intensity(i)
ds.add_field(("gas","intensity"), function=_intensity, units=self.field_units)
- prj = off_axis_projection(ds, self.center, normal, width,
- (self.nx, self.ny), "intensity")
- self.data[:,:,i] = prj[:,:]
+ if isinstance(normal, basestring):
+ prj = ds.proj("intensity", ds.coordinates.axis_id[normal])
+ buf = prj.to_frb(width, self.nx, center=self.center)["intensity"]
+ else:
+ buf = off_axis_projection(ds, self.center, normal, width,
+ (self.nx, self.ny), "intensity")
+ self.data[:,:,i] = buf[:,:]
ds.field_info.pop(("gas","intensity"))
pbar.update(i)
pbar.finish()
https://bitbucket.org/yt_analysis/yt/commits/b97cc0aaa47c/
Changeset: b97cc0aaa47c
Branch: yt
User: jzuhone
Date: 2014-10-10 20:25:49+00:00
Summary: Fixing some unit issues and trying to marginally speed things up
Affected #: 1 file
diff -r 9c9767f99106f184a0e6bec615359bb6c87fe48e -r b97cc0aaa47c1ccdf7e9e9eb9685c8509300a835 yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -100,13 +100,7 @@
los_vec = np.zeros(3)
los_vec[ds.coordinates.axis_id[normal]] = 1.0
else:
- normal = np.array(normal)
- normal /= np.sqrt(np.dot(normal, normal))
- vecs = np.identity(3)
- t = np.cross(normal, vecs).sum(axis=1)
- ax = t.argmax()
- north = np.cross(normal, vecs[ax,:]).ravel()
- orient = Orientation(normal, north_vector=north)
+ orient = Orientation(normal)
los_vec = orient.unit_vectors[2]
dd = ds.all_data()
@@ -124,17 +118,19 @@
self.vbins = np.linspace(self.v_bnd[0], self.v_bnd[1], num=self.nv+1)
self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
+ self.vmid_cgs = self.vmid.in_cgs()
self.dv = self.vbins[1]-self.vbins[0]
+ self.dv_cgs = self.dv.in_cgs()
_vlos = create_vlos(los_vec)
self.ds.field_info.add_field(("gas","v_los"), function=_vlos, units="cm/s")
if thermal_broad:
- self.v_th = lambda T: np.sqrt(2.*kboltz*T/self.particle_mass)
- self.phi_th = lambda v, T: self.dv*np.exp(-(v/self.v_th(T))**2)/(np.sqrt(np.pi)*self.v_th(T))
+ self.v2_th = lambda T: 2.*kboltz*T/self.particle_mass
+ self.phi_th = lambda v, T: self.dv_cgs*np.exp(-v*v/self.v2_th(T))/(np.sqrt(np.pi*self.v2_th(T)))
else:
- self.v_th = lambda T: 1.0
- self.phi_th = lambda v, T: np.maximum(1.-np.abs(v)/self.dv.in_units(v.units),0.0)
+ self.v2_th = lambda T: 1.0
+ self.phi_th = lambda v, T: np.maximum(1.-np.abs(v)/self.dv_cgs,0.0)
self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.field_units)
pbar = get_pbar("Generating cube.", self.nv)
@@ -152,6 +148,8 @@
pbar.update(i)
pbar.finish()
+ self.proj_units = self.data.units
+
if self.rest_value is not None:
# If we want units other than velocity, we re-calculate these quantities
self.vbins = self.rest_value*(1.-self.vbins.in_cgs()/clight)
@@ -237,14 +235,14 @@
w.wcs.ctype = [types[0],types[1],vtype]
fib = FITSImageBuffer(self.data.transpose(), fields=self.field, wcs=w)
- fib[0].header["bunit"] = self.field_units
+ fib[0].header["bunit"] = str(self.proj_units)
fib[0].header["btype"] = self.field
fib.writeto(filename, clobber=clobber)
def _create_intensity(self, i):
def _intensity(field, data):
- w = self.phi_th((self.vmid[i]-data["v_los"]).in_cgs(), data["temperature"])
+ w = self.phi_th(self.vmid_cgs[i]-data["v_los"], data["temperature"])
return data[self.field]*w
return _intensity
https://bitbucket.org/yt_analysis/yt/commits/1454d63732c7/
Changeset: 1454d63732c7
Branch: yt
User: jzuhone
Date: 2014-10-10 20:46:58+00:00
Summary: Fixing units in several places
Affected #: 3 files
diff -r b97cc0aaa47c1ccdf7e9e9eb9685c8509300a835 -r 1454d63732c755dba2e3a930ca1a67cf212d46b9 yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -18,10 +18,12 @@
from yt.funcs import get_pbar
from yt.utilities.physical_constants import clight, mh, kboltz
import yt.units.dimensions as ytdims
+import yt.units as u
from yt.units.yt_array import YTQuantity
from yt.funcs import iterable
from yt.utilities.parallel_tools.parallel_analysis_interface import \
parallel_root_only
+import re
def create_vlos(z_hat):
def _v_los(field, data):
@@ -132,7 +134,9 @@
self.v2_th = lambda T: 1.0
self.phi_th = lambda v, T: np.maximum(1.-np.abs(v)/self.dv_cgs,0.0)
- self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.field_units)
+ self.proj_units = self.field_units * u.cm.units
+
+ self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.proj_units)
pbar = get_pbar("Generating cube.", self.nv)
for i in xrange(self.nv):
_intensity = self._create_intensity(i)
@@ -148,8 +152,6 @@
pbar.update(i)
pbar.finish()
- self.proj_units = self.data.units
-
if self.rest_value is not None:
# If we want units other than velocity, we re-calculate these quantities
self.vbins = self.rest_value*(1.-self.vbins.in_cgs()/clight)
@@ -220,7 +222,9 @@
dx = self.width/self.nx
units = str(self.width.units)
# Hack because FITS is stupid and doesn't understand case
- if units == "Mpc": units = "kpc"
+ if units == "Mpc":
+ units = "kpc"
+ dx *= 1000.
dy = dx
dv = self.dv.in_units(vunit)
@@ -235,7 +239,7 @@
w.wcs.ctype = [types[0],types[1],vtype]
fib = FITSImageBuffer(self.data.transpose(), fields=self.field, wcs=w)
- fib[0].header["bunit"] = str(self.proj_units)
+ fib[0].header["bunit"] = re.sub('()', '', str(self.proj_units))
fib[0].header["btype"] = self.field
fib.writeto(filename, clobber=clobber)
diff -r b97cc0aaa47c1ccdf7e9e9eb9685c8509300a835 -r 1454d63732c755dba2e3a930ca1a67cf212d46b9 yt/analysis_modules/sunyaev_zeldovich/projection.py
--- a/yt/analysis_modules/sunyaev_zeldovich/projection.py
+++ b/yt/analysis_modules/sunyaev_zeldovich/projection.py
@@ -321,7 +321,8 @@
units = self.ds.get_smallest_appropriate_unit(self.width)
# Hack because FITS is stupid and doesn't understand case
- if units == "Mpc": units = "kpc"
+ if units == "Mpc":
+ units = "kpc"
dx = self.dx.in_units(units)
if sky_scale:
dx = (dx*sky_scale).in_units("deg")
diff -r b97cc0aaa47c1ccdf7e9e9eb9685c8509300a835 -r 1454d63732c755dba2e3a930ca1a67cf212d46b9 yt/utilities/fits_image.py
--- a/yt/utilities/fits_image.py
+++ b/yt/utilities/fits_image.py
@@ -16,6 +16,7 @@
from yt.data_objects.construction_data_containers import YTCoveringGridBase
from yt.utilities.on_demand_imports import _astropy
from yt.units.yt_array import YTQuantity
+import re
pyfits = _astropy.pyfits
pywcs = _astropy.pywcs
@@ -109,7 +110,7 @@
hdu.name = key
hdu.header["btype"] = key
if hasattr(img_data[key], "units"):
- hdu.header["bunit"] = str(img_data[key].units)
+ hdu.header["bunit"] = re.sub('()', '', str(img_data[key].units))
self.append(hdu)
self.dimensionality = len(self[0].data.shape)
https://bitbucket.org/yt_analysis/yt/commits/e1f943c57fcb/
Changeset: e1f943c57fcb
Branch: yt
User: jzuhone
Date: 2014-10-10 20:47:30+00:00
Summary: Trying to get PPV cubes recognized where the units of the lat/lon axes are in some kind of length units.
Affected #: 1 file
diff -r 1454d63732c755dba2e3a930ca1a67cf212d46b9 -r e1f943c57fcb04b70bace997e862370fe8068219 yt/frontends/fits/data_structures.py
--- a/yt/frontends/fits/data_structures.py
+++ b/yt/frontends/fits/data_structures.py
@@ -44,8 +44,8 @@
from yt.units.yt_array import YTQuantity
from yt.utilities.on_demand_imports import _astropy
-lon_prefixes = ["X","RA","GLON"]
-lat_prefixes = ["Y","DEC","GLAT"]
+lon_prefixes = ["X","RA","GLON","LINEAR"]
+lat_prefixes = ["Y","DEC","GLAT","LINEAR"]
delimiters = ["*", "/", "-", "^"]
delimiters += [str(i) for i in xrange(10)]
regex_pattern = '|'.join(re.escape(_) for _ in delimiters)
https://bitbucket.org/yt_analysis/yt/commits/090c9d808536/
Changeset: 090c9d808536
Branch: yt
User: jzuhone
Date: 2014-10-10 20:50:33+00:00
Summary: Bug fix
Affected #: 1 file
diff -r e1f943c57fcb04b70bace997e862370fe8068219 -r 090c9d80853662c9f8c275b66f7dde2464702fe3 yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -134,7 +134,7 @@
self.v2_th = lambda T: 1.0
self.phi_th = lambda v, T: np.maximum(1.-np.abs(v)/self.dv_cgs,0.0)
- self.proj_units = self.field_units * u.cm.units
+ self.proj_units = self.field_units+"*cm"
self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.proj_units)
pbar = get_pbar("Generating cube.", self.nv)
https://bitbucket.org/yt_analysis/yt/commits/4193b690a440/
Changeset: 4193b690a440
Branch: yt
User: jzuhone
Date: 2014-10-10 22:34:46+00:00
Summary: Allow for PPV cubes with actual length units to use them in plots.
Affected #: 3 files
diff -r e1f943c57fcb04b70bace997e862370fe8068219 -r 4193b690a44029933a3f07244a5b5acf97c2aedb yt/frontends/fits/data_structures.py
--- a/yt/frontends/fits/data_structures.py
+++ b/yt/frontends/fits/data_structures.py
@@ -549,7 +549,7 @@
x = 0
for p in lon_prefixes+lat_prefixes+spec_names.keys():
y = np_char.startswith(self.axis_names[:self.dimensionality], p)
- x += y.sum()
+ x += np.any(y)
if x == self.dimensionality: self._setup_spec_cube()
def _setup_spec_cube(self):
@@ -579,6 +579,12 @@
self.lon_axis = np.where(self.lon_axis)[0][0]
self.lon_name = ctypes[self.lon_axis].split("-")[0].lower()
+ if self.lat_axis == self.lon_axis and self.lat_name == self.lon_name:
+ self.lat_axis = 1
+ self.lon_axis = 0
+ self.lat_name = "Y"
+ self.lon_name = "X"
+
if self.wcs.naxis > 2:
self.spec_axis = np.zeros((end-1), dtype="bool")
diff -r e1f943c57fcb04b70bace997e862370fe8068219 -r 4193b690a44029933a3f07244a5b5acf97c2aedb yt/geometry/coordinates/spec_cube_coordinates.py
--- a/yt/geometry/coordinates/spec_cube_coordinates.py
+++ b/yt/geometry/coordinates/spec_cube_coordinates.py
@@ -26,8 +26,19 @@
self.axis_name = {}
self.axis_id = {}
- for axis, axis_name in zip([ds.lon_axis, ds.lat_axis, ds.spec_axis],
- ["Image\ x", "Image\ y", ds.spec_name]):
+ self.default_unit_label = {}
+ if ds.lon_name == "X" and ds.lat_name == "Y":
+ names = ["x","y"]
+ else:
+ names = ["Image\ x", "Image\ y"]
+ self.default_unit_label[ds.lon_axis] = "pixel"
+ self.default_unit_label[ds.lat_axis] = "pixel"
+ names.append(ds.spec_name)
+ axes = [ds.lon_axis, ds.lat_axis, ds.spec_axis]
+ self.default_unit_label[ds.spec_axis] = ds.spec_unit
+
+ for axis, axis_name in zip(axes, names):
+
lower_ax = "xyz"[axis]
upper_ax = lower_ax.upper()
@@ -40,11 +51,6 @@
self.axis_id[axis] = axis
self.axis_id[axis_name] = axis
- self.default_unit_label = {}
- self.default_unit_label[ds.lon_axis] = "pixel"
- self.default_unit_label[ds.lat_axis] = "pixel"
- self.default_unit_label[ds.spec_axis] = ds.spec_unit
-
def _spec_axis(ax, x, y):
p = (x,y)[ax]
return [self.ds.pixel2spec(pp).v for pp in p]
diff -r e1f943c57fcb04b70bace997e862370fe8068219 -r 4193b690a44029933a3f07244a5b5acf97c2aedb yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -756,9 +756,10 @@
for i, un in enumerate((unit_x, unit_y)):
if hasattr(self.ds.coordinates, "default_unit_label"):
axax = getattr(self.ds.coordinates, "%s_axis" % ("xy"[i]))[axis_index]
- un = self.ds.coordinates.default_unit_label[axax]
- axes_unit_labels[i] = '\/\/('+un+')'
- continue
+ unn = self.ds.coordinates.default_unit_label.get(axax, "")
+ if unn != "":
+ axes_unit_labels[i] = '\/\/('+unn+')'
+ continue
# Use sympy to factor h out of the unit. In this context 'un'
# is a string, so we call the Unit constructor.
expr = Unit(un, registry=self.ds.unit_registry).expr
https://bitbucket.org/yt_analysis/yt/commits/60c151c985e5/
Changeset: 60c151c985e5
Branch: yt
User: jzuhone
Date: 2014-10-10 22:35:00+00:00
Summary: Merge
Affected #: 1 file
diff -r 4193b690a44029933a3f07244a5b5acf97c2aedb -r 60c151c985e58c6eab5f93ceab30dfaef3d58c55 yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -134,7 +134,7 @@
self.v2_th = lambda T: 1.0
self.phi_th = lambda v, T: np.maximum(1.-np.abs(v)/self.dv_cgs,0.0)
- self.proj_units = self.field_units * u.cm.units
+ self.proj_units = self.field_units+"*cm"
self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.proj_units)
pbar = get_pbar("Generating cube.", self.nv)
https://bitbucket.org/yt_analysis/yt/commits/62c4c9d1b8bc/
Changeset: 62c4c9d1b8bc
Branch: yt
User: jzuhone
Date: 2014-10-13 23:18:03+00:00
Summary: This shouldn't be transposed.
Affected #: 1 file
diff -r 60c151c985e58c6eab5f93ceab30dfaef3d58c55 -r 62c4c9d1b8bcf8fe735ebb24c160cc82ba433a46 yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -238,7 +238,7 @@
w.wcs.cunit = [units,units,vunit]
w.wcs.ctype = [types[0],types[1],vtype]
- fib = FITSImageBuffer(self.data.transpose(), fields=self.field, wcs=w)
+ fib = FITSImageBuffer(self.data, fields=self.field, wcs=w)
fib[0].header["bunit"] = re.sub('()', '', str(self.proj_units))
fib[0].header["btype"] = self.field
https://bitbucket.org/yt_analysis/yt/commits/cd298f3c8e57/
Changeset: cd298f3c8e57
Branch: yt
User: jzuhone
Date: 2014-10-14 21:30:44+00:00
Summary: Rotating indices around
Affected #: 1 file
diff -r 62c4c9d1b8bcf8fe735ebb24c160cc82ba433a46 -r cd298f3c8e5764e157dffae4ec64605553c1c90e yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -146,7 +146,7 @@
buf = prj.to_frb(width, self.nx, center=self.center)["intensity"]
else:
buf = off_axis_projection(ds, self.center, normal, width,
- (self.nx, self.ny), "intensity")
+ (self.nx, self.ny), "intensity")[::-1]
self.data[:,:,i] = buf[:,:]
ds.field_info.pop(("gas","intensity"))
pbar.update(i)
@@ -238,7 +238,7 @@
w.wcs.cunit = [units,units,vunit]
w.wcs.ctype = [types[0],types[1],vtype]
- fib = FITSImageBuffer(self.data, fields=self.field, wcs=w)
+ fib = FITSImageBuffer(self.data.transpose(2,0,1), fields=self.field, wcs=w)
fib[0].header["bunit"] = re.sub('()', '', str(self.proj_units))
fib[0].header["btype"] = self.field
https://bitbucket.org/yt_analysis/yt/commits/4d04c5d2b6bd/
Changeset: 4d04c5d2b6bd
Branch: yt
User: jzuhone
Date: 2014-10-21 23:46:54+00:00
Summary: Refactoring FITSImageBuffer to make the options simpler--we only create coordinate information if a WCS is specified or if the underlying object is an FRB or covering grid.
Affected #: 3 files
diff -r cd298f3c8e5764e157dffae4ec64605553c1c90e -r 4d04c5d2b6bdb152c8ec9f1f521295ac1fd70e52 yt/analysis_modules/sunyaev_zeldovich/projection.py
--- a/yt/analysis_modules/sunyaev_zeldovich/projection.py
+++ b/yt/analysis_modules/sunyaev_zeldovich/projection.py
@@ -25,6 +25,7 @@
from yt.utilities.parallel_tools.parallel_analysis_interface import \
communication_system, parallel_root_only
from yt import units
+from yt.utilities.on_demand_imports import _astropy
import numpy as np
@@ -304,13 +305,15 @@
>>> # This example just writes out a FITS file with kpc coords
>>> szprj.write_fits("SZbullet.fits", clobber=False)
>>> # This example uses sky coords
- >>> sky_scale = 1./3600. # One arcsec per kpc
+ >>> sky_scale = (1., "arcsec/kpc") # One arcsec per kpc
>>> sky_center = (30., 45.) # In degrees
>>> szprj.write_fits("SZbullet.fits", sky_center=sky_center, sky_scale=sky_scale)
"""
+ from yt.utilities.fits_image import FITSImageBuffer
if sky_scale is None:
center = (0.0,0.0)
+ types = ["LINEAR","LINEAR"]
else:
if iterable(sky_scale):
sky_scale = self.ds.quan(sky_scale[0], sky_scale[1])
@@ -318,6 +321,7 @@
center = (30.,45.)
else:
center = sky_center
+ types = ["RA---TAN","DEC--TAN"]
units = self.ds.get_smallest_appropriate_unit(self.width)
# Hack because FITS is stupid and doesn't understand case
@@ -331,10 +335,14 @@
if sky_scale:
dx *= -1.
- from yt.utilities.fits_image import FITSImageBuffer
- fib = FITSImageBuffer(self.data, fields=self.data.keys(),
- center=center, units=units, scale=[dx.v,dy.v])
- fib.update_all_headers("Time", float(self.ds.current_time.in_units(time_units).value))
+ w = _astropy.pywcs.WCS(naxis=2)
+ w.wcs.crpix = [0.5*(self.nx+1)]*2
+ w.wcs.cdelt = [dx.v,dy.v]
+ w.wcs.crval = center
+ w.wcs.cunit = [units]*2
+ w.wcs.ctype = types
+
+ fib = FITSImageBuffer(self.data, fields=self.data.keys(), wcs=w)
fib.writeto(filename, clobber=clobber)
@parallel_root_only
diff -r cd298f3c8e5764e157dffae4ec64605553c1c90e -r 4d04c5d2b6bdb152c8ec9f1f521295ac1fd70e52 yt/utilities/fits_image.py
--- a/yt/utilities/fits_image.py
+++ b/yt/utilities/fits_image.py
@@ -28,8 +28,7 @@
class FITSImageBuffer(HDUList):
- def __init__(self, data, fields=None, units="cm",
- center=None, scale=None, wcs=None):
+ def __init__(self, data, fields=None, units="cm", wcs=None):
r""" Initialize a FITSImageBuffer object.
FITSImageBuffer contains a list of FITS ImageHDU instances, and
@@ -50,29 +49,32 @@
keys, it will use these for the fields. If *data* is just a
single array one field name must be specified.
units : string
- The units of the WCS coordinates, default "cm".
- center : array_like, optional
- The coordinates [xctr,yctr] of the images in units
- *units*. If *units* is not specified, defaults to the origin.
- scale : tuple of floats, optional
- Pixel scale in unit *units*. Will be ignored if *data* is
- a FixedResolutionBuffer or a YTCoveringGrid. Must be
- specified otherwise, or if *units* is "deg".
+ The units of the WCS coordinates. Only applies
+ to FixedResolutionBuffers or YTCoveringGrids. Defaults to "cm".
wcs : `astropy.wcs.WCS` instance, optional
- Supply an AstroPy WCS instance to override automatic WCS creation.
+ Supply an AstroPy WCS instance. Will override automatic WCS
+ creation from FixedResolutionBuffers and YTCoveringGrids.
Examples
--------
+ >>> # This example uses a FRB.
>>> ds = load("sloshing_nomag2_hdf5_plt_cnt_0150")
>>> prj = ds.proj(2, "kT", weight_field="density")
>>> frb = prj.to_frb((0.5, "Mpc"), 800)
>>> # This example just uses the FRB and puts the coords in kpc.
>>> f_kpc = FITSImageBuffer(frb, fields="kT", units="kpc")
- >>> # This example specifies sky coordinates.
- >>> scale = [1./3600.]*2 # One arcsec per pixel
- >>> f_deg = FITSImageBuffer(frb, fields="kT", units="deg",
- scale=scale, center=(30., 45.))
+ >>> # This example specifies a specific WCS.
+ >>> from astropy.wcs import WCS
+ >>> w = WCS(naxis=self.dimensionality)
+ >>> w.wcs.crval = [30., 45.] # RA, Dec in degrees
+ >>> w.wcs.cunit = ["deg"]*2
+ >>> nx, ny = 800, 800
+ >>> w.wcs.crpix = [0.5*(nx+1), 0.5*(ny+1)]
+ >>> w.wcs.ctype = ["RA---TAN","DEC--TAN"]
+ >>> scale = 1./3600. # One arcsec per pixel
+ >>> w.wcs.cdelt = [-scale, scale]
+ >>> f_deg = FITSImageBuffer(frb, fields="kT", wcs=w)
>>> f_deg.writeto("temp.fits")
"""
@@ -122,25 +124,11 @@
has_coords = (isinstance(img_data, FixedResolutionBuffer) or
isinstance(img_data, YTCoveringGridBase))
-
- if center is None:
- if units == "deg":
- mylog.error("Please specify center=(RA, Dec) in degrees.")
- raise ValueError
- elif not has_coords:
- mylog.warning("Setting center to the origin.")
- center = [0.0]*self.dimensionality
-
- if scale is None:
- if units == "deg" or not has_coords and wcs is None:
- mylog.error("Please specify scale=(dx,dy[,dz]) in %s." % (units))
- raise ValueError
if wcs is None:
w = pywcs.WCS(header=self[0].header, naxis=self.dimensionality)
w.wcs.crpix = 0.5*(np.array(self.shape)+1)
- proj_type = ["linear"]*self.dimensionality
- if isinstance(img_data, FixedResolutionBuffer) and units != "deg":
+ if isinstance(img_data, FixedResolutionBuffer):
# FRBs are a special case where we have coordinate
# information, so we take advantage of this and
# construct the WCS object
@@ -152,28 +140,20 @@
elif isinstance(img_data, YTCoveringGridBase):
dx, dy, dz = img_data.dds.in_units(units)
center = 0.5*(img_data.left_edge+img_data.right_edge).in_units(units)
- elif units == "deg" and self.dimensionality == 2:
- dx = -scale[0]
- dy = scale[1]
- proj_type = ["RA---TAN","DEC--TAN"]
else:
- dx = scale[0]
- dy = scale[1]
- if self.dimensionality == 3: dz = scale[2]
-
+ # We default to pixel coordinates if nothing is provided
+ dx, dy, dz = 1.0, 1.0, 1.0
+ center = 0.5*(np.array(self.shape)+1)
w.wcs.crval = center
- w.wcs.cunit = [units]*self.dimensionality
- w.wcs.ctype = proj_type
-
+ if has_coords:
+ w.wcs.cunit = [units]*self.dimensionality
if self.dimensionality == 2:
w.wcs.cdelt = [dx,dy]
elif self.dimensionality == 3:
w.wcs.cdelt = [dx,dy,dz]
-
+ w.wcs.ctype = ["linear"]*self.dimensionality
self._set_wcs(w)
-
else:
-
self._set_wcs(wcs)
def _set_wcs(self, wcs):
diff -r cd298f3c8e5764e157dffae4ec64605553c1c90e -r 4d04c5d2b6bdb152c8ec9f1f521295ac1fd70e52 yt/visualization/volume_rendering/image_handling.py
--- a/yt/visualization/volume_rendering/image_handling.py
+++ b/yt/visualization/volume_rendering/image_handling.py
@@ -40,9 +40,7 @@
data["b"] = image[:,:,2]
data["a"] = image[:,:,3]
nx, ny = data["r"].shape
- fib = FITSImageBuffer(data, units="pixel",
- center=[0.5*(nx+1), 0.5*(ny+1)],
- scale=[1.]*2)
+ fib = FITSImageBuffer(data)
fib.writeto('%s.fits'%fn,clobber=True)
def import_rgba(name, h5=True):
https://bitbucket.org/yt_analysis/yt/commits/0113185edacf/
Changeset: 0113185edacf
Branch: yt
User: jzuhone
Date: 2014-10-22 00:07:46+00:00
Summary: Small improvements
Affected #: 1 file
diff -r 4d04c5d2b6bdb152c8ec9f1f521295ac1fd70e52 -r 0113185edacf2b757d0ca2c3bdad85a63c613f44 yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -41,7 +41,7 @@
class PPVCube(object):
def __init__(self, ds, normal, field, center="c", width=(1.0,"unitary"),
dims=(100,100,100), velocity_bounds=None, rest_value=None,
- thermal_broad=False, particle_weight=56.):
+ thermal_broad=False, atomic_weight=56.):
r""" Initialize a PPVCube object.
Parameters
@@ -55,7 +55,7 @@
field : string
The field to project.
center : float, tuple, or string
-
+ The coordinates of the dataset *ds* on which to center the PPVCube.
width : float or tuple, optional
The width of the projection in length units. Specify a float
for code_length units or a tuple (value, units).
@@ -70,7 +70,7 @@
axis of the PPV cube. The spectral axis will be converted to the units and
displaced by the value. Can be in units of energy, wavelength, or frequency.
If not set the default is to leave the spectral axis in velocity units.
- particle_weight : float, optional
+ atomic_weight : float, optional
Set this value to the atomic weight of the particle that is emitting the line
if *thermal_broad* is True. Defaults to 56 (Fe).
@@ -89,7 +89,7 @@
self.rest_value = rest_value
else:
self.rest_value = ds.quan(rest_value[0], rest_value[1])
- self.particle_mass = particle_weight*mh
+ self.particle_mass = atomic_weight*mh
self.thermal_broad = thermal_broad
self.center = ds.coordinates.sanitize_center(center, normal)[0]
@@ -216,24 +216,24 @@
v_center = 0.5*(self.vbins[0]+self.vbins[-1]).in_units(vunit).value
if sky_scale:
- dx = (self.width*sky_scale).in_units("deg")/self.nx
+ dx = (self.width*sky_scale).in_units("deg").v/self.nx
units = "deg"
else:
- dx = self.width/self.nx
- units = str(self.width.units)
+ units = str(self.ds.get_smallest_appropriate_unit(self.width))
+ dx = self.width.v/self.nx
# Hack because FITS is stupid and doesn't understand case
if units == "Mpc":
units = "kpc"
dx *= 1000.
dy = dx
- dv = self.dv.in_units(vunit)
+ dv = self.dv.in_units(vunit).v
if sky_scale:
dx *= -1.
w = _astropy.pywcs.WCS(naxis=3)
w.wcs.crpix = [0.5*(self.nx+1), 0.5*(self.ny+1), 0.5*(self.nv+1)]
- w.wcs.cdelt = [dx.v,dy.v,dv.v]
+ w.wcs.cdelt = [dx,dy,dv]
w.wcs.crval = [center[0],center[1],v_center]
w.wcs.cunit = [units,units,vunit]
w.wcs.ctype = [types[0],types[1],vtype]
https://bitbucket.org/yt_analysis/yt/commits/fe25428c10cc/
Changeset: fe25428c10cc
Branch: yt
User: jzuhone
Date: 2014-10-22 00:20:54+00:00
Summary: Some more small improvements
Affected #: 1 file
diff -r 0113185edacf2b757d0ca2c3bdad85a63c613f44 -r fe25428c10cc24caa03497ede62615b2717c292e yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -102,7 +102,13 @@
los_vec = np.zeros(3)
los_vec[ds.coordinates.axis_id[normal]] = 1.0
else:
- orient = Orientation(normal)
+ normal = np.array(normal)
+ normal /= np.sqrt(np.dot(normal, normal))
+ vecs = np.identity(3)
+ t = np.cross(normal, vecs).sum(axis=1)
+ ax = t.argmax()
+ north = np.cross(normal, vecs[ax,:]).ravel()
+ orient = Orientation(normal, north_vector=north)
los_vec = orient.unit_vectors[2]
dd = ds.all_data()
@@ -176,8 +182,8 @@
self.width = ds.quan(self.width, "code_length")
@parallel_root_only
- def write_fits(self, filename, clobber=True, sky_scale=None,
- sky_center=None):
+ def write_fits(self, filename, clobber=True, length_unit=None,
+ sky_scale=None, sky_center=None):
r""" Write the PPVCube to a FITS file.
Parameters
@@ -186,6 +192,8 @@
The name of the file to write.
clobber : boolean
Whether or not to clobber an existing file with the same name.
+ length_unit : string
+ The units to convert the coordinates to in the file.
sky_scale : tuple or YTQuantity
Conversion between an angle unit and a length unit, if sky
coordinates are desired.
@@ -219,12 +227,17 @@
dx = (self.width*sky_scale).in_units("deg").v/self.nx
units = "deg"
else:
- units = str(self.ds.get_smallest_appropriate_unit(self.width))
+ if length_unit is None:
+ units = str(self.ds.get_smallest_appropriate_unit(self.width))
+ else:
+ units = length_unit
dx = self.width.v/self.nx
- # Hack because FITS is stupid and doesn't understand case
+ # Hacks because FITS is stupid and doesn't understand case
if units == "Mpc":
units = "kpc"
dx *= 1000.
+ elif units == "au":
+ units = "AU"
dy = dx
dv = self.dv.in_units(vunit).v
https://bitbucket.org/yt_analysis/yt/commits/88cbf01c5e36/
Changeset: 88cbf01c5e36
Branch: yt
User: jzuhone
Date: 2014-10-22 00:39:59+00:00
Summary: Bug fixes
Affected #: 1 file
diff -r fe25428c10cc24caa03497ede62615b2717c292e -r 88cbf01c5e36312567488636451181d123ac1ee5 yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -231,7 +231,7 @@
units = str(self.ds.get_smallest_appropriate_unit(self.width))
else:
units = length_unit
- dx = self.width.v/self.nx
+ dx = self.width.in_units(units).v/self.nx
# Hacks because FITS is stupid and doesn't understand case
if units == "Mpc":
units = "kpc"
@@ -251,7 +251,7 @@
w.wcs.cunit = [units,units,vunit]
w.wcs.ctype = [types[0],types[1],vtype]
- fib = FITSImageBuffer(self.data.transpose(2,0,1), fields=self.field, wcs=w)
+ fib = FITSImageBuffer(self.data.transpose(), fields=self.field, wcs=w)
fib[0].header["bunit"] = re.sub('()', '', str(self.proj_units))
fib[0].header["btype"] = self.field
https://bitbucket.org/yt_analysis/yt/commits/79c073d69c59/
Changeset: 79c073d69c59
Branch: yt
User: jzuhone
Date: 2014-10-22 00:49:36+00:00
Summary: Bug fixes
Affected #: 1 file
diff -r 88cbf01c5e36312567488636451181d123ac1ee5 -r 79c073d69c59a044a3d07cd2bbd01aac94981eb4 yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -140,7 +140,7 @@
self.v2_th = lambda T: 1.0
self.phi_th = lambda v, T: np.maximum(1.-np.abs(v)/self.dv_cgs,0.0)
- self.proj_units = self.field_units+"*cm"
+ self.proj_units = str(ds.quan(1.0, self.field_units+"*cm").units)
self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.proj_units)
pbar = get_pbar("Generating cube.", self.nv)
@@ -260,6 +260,7 @@
def _create_intensity(self, i):
def _intensity(field, data):
w = self.phi_th(self.vmid_cgs[i]-data["v_los"], data["temperature"])
+ w[np.isnan(w)] = 0.0
return data[self.field]*w
return _intensity
https://bitbucket.org/yt_analysis/yt/commits/8498a9431dd7/
Changeset: 8498a9431dd7
Branch: yt
User: jzuhone
Date: 2014-10-22 02:32:27+00:00
Summary: Moved the spectral axis transformation to its own method.
Affected #: 1 file
diff -r 79c073d69c59a044a3d07cd2bbd01aac94981eb4 -r 8498a9431dd7ea87c831f70833035cdefaf2fd6d yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -40,8 +40,8 @@
class PPVCube(object):
def __init__(self, ds, normal, field, center="c", width=(1.0,"unitary"),
- dims=(100,100,100), velocity_bounds=None, rest_value=None,
- thermal_broad=False, atomic_weight=56.):
+ dims=(100,100,100), velocity_bounds=None, thermal_broad=False,
+ atomic_weight=56.):
r""" Initialize a PPVCube object.
Parameters
@@ -65,11 +65,6 @@
A 3-tuple of (vmin, vmax, units) for the velocity bounds to
integrate over. If None, the largest velocity of the
dataset will be used, e.g. velocity_bounds = (-v.max(), v.max())
- rest_value : tuple or YTQuantity, optional
- A (value, unit) tuple or YTQuantity indicating the rest value of the spectral
- axis of the PPV cube. The spectral axis will be converted to the units and
- displaced by the value. Can be in units of energy, wavelength, or frequency.
- If not set the default is to leave the spectral axis in velocity units.
atomic_weight : float, optional
Set this value to the atomic weight of the particle that is emitting the line
if *thermal_broad* is True. Defaults to 56 (Fe).
@@ -85,10 +80,6 @@
self.ds = ds
self.field = field
self.width = width
- if isinstance(rest_value, YTQuantity) or rest_value is None:
- self.rest_value = rest_value
- else:
- self.rest_value = ds.quan(rest_value[0], rest_value[1])
self.particle_mass = atomic_weight*mh
self.thermal_broad = thermal_broad
@@ -125,6 +116,7 @@
ds.quan(velocity_bounds[1], velocity_bounds[2]))
self.vbins = np.linspace(self.v_bnd[0], self.v_bnd[1], num=self.nv+1)
+ self._vbins = self.vbins.copy()
self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
self.vmid_cgs = self.vmid.in_cgs()
self.dv = self.vbins[1]-self.vbins[0]
@@ -158,14 +150,31 @@
pbar.update(i)
pbar.finish()
- if self.rest_value is not None:
- # If we want units other than velocity, we re-calculate these quantities
- self.vbins = self.rest_value*(1.-self.vbins.in_cgs()/clight)
- self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
- self.dv = self.vbins[1]-self.vbins[0]
+ self.axis_type = "velocity"
+ # Now fix the width
+ if iterable(self.width):
+ self.width = ds.quan(self.width[0], self.width[1])
+ else:
+ self.width = ds.quan(self.width, "code_length")
+
+ def transform_spectral_axis(self, rest_value, units):
+ """
+ Change the units of the spectral axis to some equivalent unit, such
+ as energy, wavelength, or frequency, by providing a *rest_value* and the
+ *units* of the new spectral axis. This corresponds to the Doppler-shifting
+ of lines due to gas motions and thermal broadening.
+ """
+ if self.axis_type != "velocity":
+ self.reset_spectral_axis()
+ x0 = self.ds.quan(rest_value, units)
+ if x0.units.dimensions == ytdims.rate or x0.units.dimensions == ytdims.energy:
+ self.vbins = x0*(1.-self.vbins.in_cgs()/clight)
+ elif x0.units.dimensions == ytdims.length:
+ self.vbins = x0/(1.-self.vbins.in_cgs()/clight)
+ self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
+ self.dv = self.vbins[1]-self.vbins[0]
dims = self.dv.units.dimensions
-
if dims == ytdims.rate:
self.axis_type = "frequency"
elif dims == ytdims.length:
@@ -175,11 +184,13 @@
elif dims == ytdims.velocity:
self.axis_type = "velocity"
- # Now fix the width
- if iterable(self.width):
- self.width = ds.quan(self.width[0], self.width[1])
- else:
- self.width = ds.quan(self.width, "code_length")
+ def reset_spectral_axis(self):
+ """
+ Reset the spectral axis to the original velocity range and units.
+ """
+ self.vbins = self._vbins.copy()
+ self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
+ self.dv = self.vbins[1]-self.vbins[0]
@parallel_root_only
def write_fits(self, filename, clobber=True, length_unit=None,
https://bitbucket.org/yt_analysis/yt/commits/5d42ecd21485/
Changeset: 5d42ecd21485
Branch: yt
User: jzuhone
Date: 2014-10-22 14:20:00+00:00
Summary: Bringing PPVCube docs up-to-date
Affected #: 1 file
diff -r 8498a9431dd7ea87c831f70833035cdefaf2fd6d -r 5d42ecd2148572be9cec39c552443987aece9ffd doc/source/analyzing/analysis_modules/PPVCube.ipynb
--- a/doc/source/analyzing/analysis_modules/PPVCube.ipynb
+++ b/doc/source/analyzing/analysis_modules/PPVCube.ipynb
@@ -1,7 +1,7 @@
{
"metadata": {
"name": "",
- "signature": "sha256:56a8d72735e3cc428ff04b241d4b2ce6f653019818c6fc7a4148840d99030c85"
+ "signature": "sha256:b83e125278c2e58da4d99ac9d2ca2a136d01f1094e1b83497925e0f9b9b056c2"
},
"nbformat": 3,
"nbformat_minor": 0,
@@ -32,7 +32,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "To demonstrate this functionality, we'll create a simple unigrid dataset from scratch of a rotating disk galaxy. We create a thin disk in the x-y midplane of the domain of three cells in height in either direction, and a radius of 10 kpc. The density and azimuthal velocity profiles of the disk as a function of radius will be given by the following functions:"
+ "To demonstrate this functionality, we'll create a simple unigrid dataset from scratch of a rotating disk. We create a thin disk in the x-y midplane of the domain of three cells in height in either direction, and a radius of 10 kpc. The density and azimuthal velocity profiles of the disk as a function of radius will be given by the following functions:"
]
},
{
@@ -84,7 +84,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Second, we'll construct the data arrays for the density and the velocity of the disk. Since we have the tangential velocity profile, we have to use the polar coordinates we derived earlier to compute `velx` and `vely`. Everywhere outside the disk, all fields are set to zero. "
+ "Second, we'll construct the data arrays for the density, temperature, and velocity of the disk. Since we have the tangential velocity profile, we have to use the polar coordinates we derived earlier to compute `velx` and `vely`. Everywhere outside the disk, all fields are set to zero. "
]
},
{
@@ -93,12 +93,15 @@
"input": [
"dens = np.zeros((nx,ny,nz))\n",
"dens[:,:,nz/2-3:nz/2+3] = (r**alpha).reshape(nx,ny,1) # the density profile of the disk\n",
- "vel_theta = r/(1.+(r/r_0)**beta) # the azimuthal velocity profile of the disk\n",
+ "temp = np.zeros((nx,ny,nz))\n",
+ "temp[:,:,nz/2-3:nz/2+3] = 1.0e5 # Isothermal\n",
+ "vel_theta = 100.*r/(1.+(r/r_0)**beta) # the azimuthal velocity profile of the disk\n",
"velx = np.zeros((nx,ny,nz))\n",
"vely = np.zeros((nx,ny,nz))\n",
"velx[:,:,nz/2-3:nz/2+3] = (-vel_theta*np.sin(theta)).reshape(nx,ny,1) # convert polar to cartesian\n",
"vely[:,:,nz/2-3:nz/2+3] = (vel_theta*np.cos(theta)).reshape(nx,ny,1) # convert polar to cartesian\n",
"dens[r > R] = 0.0\n",
+ "temp[r > R] = 0.0\n",
"velx[r > R] = 0.0\n",
"vely[r > R] = 0.0"
],
@@ -119,6 +122,7 @@
"input": [
"data = {}\n",
"data[\"density\"] = (dens,\"g/cm**3\")\n",
+ "data[\"temperature\"] = (temp, \"K\")\n",
"data[\"velocity_x\"] = (velx, \"km/s\")\n",
"data[\"velocity_y\"] = (vely, \"km/s\")\n",
"data[\"velocity_z\"] = (np.zeros((nx,ny,nz)), \"km/s\") # zero velocity in the z-direction\n",
@@ -189,7 +193,7 @@
"cell_type": "code",
"collapsed": false,
"input": [
- "cube = PPVCube(ds, L, \"density\", dims=(200,100,50), velocity_bounds=(-1.5,1.5,\"km/s\"))"
+ "cube = PPVCube(ds, L, \"density\", dims=(200,100,50), velocity_bounds=(-150.,150.,\"km/s\"))"
],
"language": "python",
"metadata": {},
@@ -199,14 +203,33 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Following this, we can now write this cube to a FITS file:"
+ "Following this, we can now write this cube to a FITS file. The x and y axes of the file can be in length units, which can be optionally specified by `length_unit`:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
- "cube.write_fits(\"cube.fits\", clobber=True, length_unit=(5.0,\"deg\"))"
+ "cube.write_fits(\"cube.fits\", clobber=True, length_unit=\"kpc\")"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Or one can use the `sky_scale` and `sky_center` keywords to set up the coordinates in RA and Dec:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "sky_scale = (1.0, \"arcsec/kpc\")\n",
+ "sky_center = (30., 45.) # RA, Dec in degrees\n",
+ "cube.write_fits(\"cube_sky.fits\", clobber=True, sky_scale=sky_scale, sky_center=sky_center)"
],
"language": "python",
"metadata": {},
@@ -223,7 +246,7 @@
"cell_type": "code",
"collapsed": false,
"input": [
- "ds = yt.load(\"cube.fits\")"
+ "ds_cube = yt.load(\"cube.fits\")"
],
"language": "python",
"metadata": {},
@@ -234,7 +257,7 @@
"collapsed": false,
"input": [
"# Specifying no center gives us the center slice\n",
- "slc = yt.SlicePlot(ds, \"z\", [\"density\"])\n",
+ "slc = yt.SlicePlot(ds_cube, \"z\", [\"density\"])\n",
"slc.show()"
],
"language": "python",
@@ -247,9 +270,9 @@
"input": [
"import yt.units as u\n",
"# Picking different velocities for the slices\n",
- "new_center = ds.domain_center\n",
- "new_center[2] = ds.spec2pixel(-1.0*u.km/u.s)\n",
- "slc = yt.SlicePlot(ds, \"z\", [\"density\"], center=new_center)\n",
+ "new_center = ds_cube.domain_center\n",
+ "new_center[2] = ds_cube.spec2pixel(-100.*u.km/u.s)\n",
+ "slc = yt.SlicePlot(ds_cube, \"z\", [\"density\"], center=new_center)\n",
"slc.show()"
],
"language": "python",
@@ -260,8 +283,8 @@
"cell_type": "code",
"collapsed": false,
"input": [
- "new_center[2] = ds.spec2pixel(0.7*u.km/u.s)\n",
- "slc = yt.SlicePlot(ds, \"z\", [\"density\"], center=new_center)\n",
+ "new_center[2] = ds_cube.spec2pixel(70.0*u.km/u.s)\n",
+ "slc = yt.SlicePlot(ds_cube, \"z\", [\"density\"], center=new_center)\n",
"slc.show()"
],
"language": "python",
@@ -272,8 +295,8 @@
"cell_type": "code",
"collapsed": false,
"input": [
- "new_center[2] = ds.spec2pixel(-0.3*u.km/u.s)\n",
- "slc = yt.SlicePlot(ds, \"z\", [\"density\"], center=new_center)\n",
+ "new_center[2] = ds_cube.spec2pixel(-30.0*u.km/u.s)\n",
+ "slc = yt.SlicePlot(ds_cube, \"z\", [\"density\"], center=new_center)\n",
"slc.show()"
],
"language": "python",
@@ -291,7 +314,7 @@
"cell_type": "code",
"collapsed": false,
"input": [
- "prj = yt.ProjectionPlot(ds, \"z\", [\"density\"], method=\"sum\")\n",
+ "prj = yt.ProjectionPlot(ds_cube, \"z\", [\"density\"], method=\"sum\")\n",
"prj.set_log(\"density\", True)\n",
"prj.set_zlim(\"density\", 1.0e-3, 0.2)\n",
"prj.show()"
@@ -299,9 +322,100 @@
"language": "python",
"metadata": {},
"outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The `thermal_broad` keyword allows one to simulate thermal line broadening based on the temperature, and the `atomic_weight` argument is used to specify the atomic weight of the particle that is doing the emitting."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "cube2 = PPVCube(ds, L, \"density\", dims=(200,100,50), velocity_bounds=(-150,150,\"km/s\"), thermal_broad=True, \n",
+ " atomic_weight=12.0)\n",
+ "cube2.write_fits(\"cube2.fits\", clobber=True, length_unit=\"kpc\")"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Taking a slice of this cube shows:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "ds_cube2 = yt.load(\"cube2.fits\")\n",
+ "new_center = ds_cube2.domain_center\n",
+ "new_center[2] = ds_cube2.spec2pixel(70.0*u.km/u.s)\n",
+ "slc = yt.SlicePlot(ds_cube2, \"z\", [\"density\"], center=new_center)\n",
+ "slc.show()"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "new_center[2] = ds_cube2.spec2pixel(-100.*u.km/u.s)\n",
+ "slc = yt.SlicePlot(ds_cube2, \"z\", [\"density\"], center=new_center)\n",
+ "slc.show()"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "where we can see the emission has been smeared into this velocity slice from neighboring slices due to the thermal broadening. \n",
+ "\n",
+ "Finally, the \"velocity\" or \"spectral\" axis of the cube can be changed to a different unit, such as wavelength, frequency, or energy: "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "print cube2.vbins[0], cube2.vbins[-1]\n",
+ "cube2.transform_spectral_axis(400.0,\"nm\")\n",
+ "print cube2.vbins[0], cube2.vbins[-1]"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If a FITS file is now written from the cube, the spectral axis will be in the new units. To reset the spectral axis back to the original velocity units:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "cube2.reset_spectral_axis()\n",
+ "print cube2.vbins[0], cube2.vbins[-1]"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
}
],
"metadata": {}
}
]
-}
+}
\ No newline at end of file
https://bitbucket.org/yt_analysis/yt/commits/93d77640b416/
Changeset: 93d77640b416
Branch: yt
User: jzuhone
Date: 2014-10-22 14:24:45+00:00
Summary: Merge
Affected #: 38 files
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 doc/install_script.sh
--- a/doc/install_script.sh
+++ b/doc/install_script.sh
@@ -987,6 +987,7 @@
if !( ( ${DEST_DIR}/bin/python2.7 -c "import readline" 2>&1 )>> ${LOG_FILE})
then
if !( ( ${DEST_DIR}/bin/python2.7 -c "import gnureadline" 2>&1 )>> ${LOG_FILE})
+ then
echo "Installing pure-python readline"
( ${DEST_DIR}/bin/pip install gnureadline 2>&1 ) 1>> ${LOG_FILE}
fi
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 doc/source/analyzing/analysis_modules/halo_transition.rst
--- a/doc/source/analyzing/analysis_modules/halo_transition.rst
+++ b/doc/source/analyzing/analysis_modules/halo_transition.rst
@@ -52,7 +52,7 @@
data_ds = yt.load('Enzo_64/RD0006/RedshiftOutput0006')
hc = HaloCatalog(data_ds=data_ds, finder_method='hop')
hc.create()
- ad = hc.all_data()
+ ad = hc.halos_ds.all_data()
masses = ad['particle_mass'][:]
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 doc/source/cookbook/gadget_notebook.rst
--- /dev/null
+++ b/doc/source/cookbook/gadget_notebook.rst
@@ -0,0 +1,7 @@
+.. _gadget-notebook:
+
+Using yt to view and analyze Gadget outputs
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+.. notebook:: yt_gadget_analysis.ipynb
+
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 doc/source/cookbook/tipsy_and_yt.ipynb
--- a/doc/source/cookbook/tipsy_and_yt.ipynb
+++ b/doc/source/cookbook/tipsy_and_yt.ipynb
@@ -1,7 +1,16 @@
{
"metadata": {
+ "kernelspec": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 2
+ },
+ "display_name": "IPython (Python 2)",
+ "language": "python",
+ "name": "python2"
+ },
"name": "",
- "signature": "sha256:2ae8b1599fa35495fa1bb8deb1c67094e3529e70093b30e20354122cd9403d9d"
+ "signature": "sha256:1f6e5cf50123ad75676f035a2a36cd60f4987832462907b9cb78cb25548d8afd"
},
"nbformat": 3,
"nbformat_minor": 0,
@@ -10,14 +19,6 @@
"cells": [
{
"cell_type": "heading",
- "level": 1,
- "metadata": {},
- "source": [
- "Using yt to view and analyze Tipsy outputs from Gasoline"
- ]
- },
- {
- "cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
@@ -193,4 +194,4 @@
"metadata": {}
}
]
-}
+}
\ No newline at end of file
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 doc/source/cookbook/yt_gadget_analysis.ipynb
--- /dev/null
+++ b/doc/source/cookbook/yt_gadget_analysis.ipynb
@@ -0,0 +1,263 @@
+{
+ "metadata": {
+ "kernelspec": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 2
+ },
+ "display_name": "IPython (Python 2)",
+ "language": "python",
+ "name": "python2"
+ },
+ "name": "",
+ "signature": "sha256:42e2b7cc4c70a501432f24bc0d62d0723605d50196399148dd365d28387dd55d"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+ {
+ "cells": [
+ {
+ "cell_type": "heading",
+ "level": 2,
+ "metadata": {},
+ "source": [
+ "Loading the data"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "First we set up our imports:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "import yt\n",
+ "import numpy as np\n",
+ "import yt.units as units\n",
+ "import pylab"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "First we load the data set, specifying both the unit length/mass/velocity, as well as the size of the bounding box (which should encapsulate all the particles in the data set)\n",
+ "\n",
+ "At the end, we flatten the data into \"ad\" in case we want access to the raw simulation data"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ ">This dataset is available for download at http://yt-project.org/data/GadgetDiskGalaxy.tar.gz (430 MB)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "fname = 'GadgetDiskGalaxy/snapshot_200.hdf5'\n",
+ "\n",
+ "unit_base = {'UnitLength_in_cm' : 3.08568e+21,\n",
+ " 'UnitMass_in_g' : 1.989e+43,\n",
+ " 'UnitVelocity_in_cm_per_s' : 100000}\n",
+ "\n",
+ "bbox_lim = 1e5 #kpc\n",
+ "\n",
+ "bbox = [[-bbox_lim,bbox_lim],\n",
+ " [-bbox_lim,bbox_lim],\n",
+ " [-bbox_lim,bbox_lim]]\n",
+ " \n",
+ "ds = yt.load(fname,unit_base=unit_base,bounding_box=bbox)\n",
+ "ds.index\n",
+ "ad= ds.all_data()"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's make a projection plot to look at the entire volume"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "px = yt.ProjectionPlot(ds, 'x', ('gas', 'density'))\n",
+ "px.show()"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's print some quantities about the domain, as well as the physical properties of the simulation\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "print 'left edge: ',ds.domain_left_edge\n",
+ "print 'right edge: ',ds.domain_right_edge\n",
+ "print 'center: ',ds.domain_center"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can also see the fields that are available to query in the dataset"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "sorted(ds.field_list)"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's create a data object that represents the full simulation domain, and find the total mass in gas and dark matter particles contained in it:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "ad = ds.all_data()\n",
+ "\n",
+ "# total_mass returns a list, representing the total gas and dark matter + stellar mass, respectively\n",
+ "print [tm.in_units('Msun') for tm in ad.quantities.total_mass()]"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now let's say we want to zoom in on the box (since clearly the bounding we chose initially is much larger than the volume containing the gas particles!), and center on wherever the highest gas density peak is. First, let's find this peak:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "density = ad[(\"PartType0\",\"density\")]\n",
+ "wdens = np.where(density == np.max(density))\n",
+ "coordinates = ad[(\"PartType0\",\"Coordinates\")]\n",
+ "center = coordinates[wdens][0]\n",
+ "print 'center = ',center"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Set up the box to zoom into"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "new_box_size = ds.quan(250,'code_length')\n",
+ "\n",
+ "left_edge = center - new_box_size/2\n",
+ "right_edge = center + new_box_size/2\n",
+ "\n",
+ "print new_box_size.in_units('Mpc')\n",
+ "print left_edge.in_units('Mpc')\n",
+ "print right_edge.in_units('Mpc')"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "ad2= ds.region(center=center, left_edge=left_edge, right_edge=right_edge)"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Using this new data object, let's confirm that we're only looking at a subset of the domain by first calculating thte total mass in gas and particles contained in the subvolume:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "print [tm.in_units('Msun') for tm in ad.quantities.total_mass()]"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "And then by visualizing what the new zoomed region looks like"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "px = yt.ProjectionPlot(ds, 'x', ('gas', 'density'), center=center, width=new_box_size)\n",
+ "px.show()"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Cool - there's a disk galaxy there!"
+ ]
+ }
+ ],
+ "metadata": {}
+ }
+ ]
+}
\ No newline at end of file
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 doc/source/developing/creating_frontend.rst
--- a/doc/source/developing/creating_frontend.rst
+++ b/doc/source/developing/creating_frontend.rst
@@ -7,14 +7,14 @@
have a question about making a custom derived quantity, please
contact the mailing list.
-yt is designed to support analysis and visualization of data from multiple
-different simulation codes, although it has so far been most successfully
-applied to Adaptive Mesh Refinement (AMR) data. For a list of codes and the
-level of support they enjoy, see :ref:`code-support`.
+yt is designed to support analysis and visualization of data from
+multiple different simulation codes. For a list of codes and the level
+of support they enjoy, see :ref:`code-support`.
-We'd like to support a broad range of codes, both AMR-based and otherwise. To
-add support for a new code, a few things need to be put into place. These
-necessary structures can be classified into a couple categories:
+We'd like to support a broad range of codes, both Adaptive Mesh
+Refinement (AMR)-based and otherwise. To add support for a new code, a
+few things need to be put into place. These necessary structures can
+be classified into a couple categories:
* Data meaning: This is the set of parameters that convert the data into
physically relevant units; things like spatial and mass conversions, time
@@ -33,73 +33,147 @@
If you are interested in adding a new code, be sure to drop us a line on
`yt-dev <http://lists.spacepope.org/listinfo.cgi/yt-dev-spacepope.org>`_!
-To get started, make a new directory in ``yt/frontends`` with the name of your
-code -- you can start by copying into it the contents of the ``stream``
-directory, which is a pretty empty format. You'll then have to create a subclass
-of ``Dataset``. This subclass will need to handle conversion between the
-different physical units and the code units; for the most part, the examples of
-``OrionDataset`` and ``EnzoDataset`` should be followed, but
-``ChomboDataset``, as a slightly newer addition, can also be used as an
-instructive example -- be sure to add an ``_is_valid`` classmethod that will
-verify if a filename is valid for that output type, as that is how "load" works.
+To get started, make a new directory in ``yt/frontends`` with the name
+of your code. Copying the contents of the ``yt/frontends/_skeleton``
+directory will add a lot of boilerplate for the required classes and
+methods that are needed. In particular, you'll have to create a
+subclass of ``Dataset`` in the data_structures.py file. This subclass
+will need to handle conversion between the different physical units
+and the code units (typically in the ``_set_code_unit_attributes()``
+method), read in metadata describing the overall data on disk (via the
+``_parse_parameter_file()`` method), and provide a ``classmethod``
+called ``_is_valid()`` that lets the ``yt.load`` method help identify an
+input file as belonging to *this* particular ``Dataset`` subclass.
+For the most part, the examples of
+``yt.frontends.boxlib.data_structures.OrionDataset`` and
+``yt.frontends.enzo.data_structures.EnzoDataset`` should be followed,
+but ``yt.frontends.chombo.data_structures.ChomboDataset``, as a
+slightly newer addition, can also be used as an instructive example.
-A new set of fields must be added in the file ``fields.py`` in that directory.
-For the most part this means subclassing ``CodeFieldInfoContainer`` and adding
-the necessary fields specific to that code. Here is the Chombo field container:
+A new set of fields must be added in the file ``fields.py`` in your
+new directory. For the most part this means subclassing
+``FieldInfoContainer`` and adding the necessary fields specific to
+your code. Here is a snippet from the base BoxLib field container:
.. code-block:: python
- from UniversalFields import *
- class ChomboFieldContainer(CodeFieldInfoContainer):
- _shared_state = {}
- _field_list = {}
- ChomboFieldInfo = ChomboFieldContainer()
- add_chombo_field = ChomboFieldInfo.add_field
+ from yt.fields.field_info_container import FieldInfoContainer
+ class BoxlibFieldInfo(FieldInfoContainer):
+ known_other_fields = (
+ ("density", (rho_units, ["density"], None)),
+ ("eden", (eden_units, ["energy_density"], None)),
+ ("xmom", (mom_units, ["momentum_x"], None)),
+ ("ymom", (mom_units, ["momentum_y"], None)),
+ ("zmom", (mom_units, ["momentum_z"], None)),
+ ("temperature", ("K", ["temperature"], None)),
+ ("Temp", ("K", ["temperature"], None)),
+ ("x_velocity", ("cm/s", ["velocity_x"], None)),
+ ("y_velocity", ("cm/s", ["velocity_y"], None)),
+ ("z_velocity", ("cm/s", ["velocity_z"], None)),
+ ("xvel", ("cm/s", ["velocity_x"], None)),
+ ("yvel", ("cm/s", ["velocity_y"], None)),
+ ("zvel", ("cm/s", ["velocity_z"], None)),
+ )
-The field container is a shared state object, which is why we explicitly set
-``_shared_state`` equal to a mutable.
+ known_particle_fields = (
+ ("particle_mass", ("code_mass", [], None)),
+ ("particle_position_x", ("code_length", [], None)),
+ ("particle_position_y", ("code_length", [], None)),
+ ("particle_position_z", ("code_length", [], None)),
+ ("particle_momentum_x", (mom_units, [], None)),
+ ("particle_momentum_y", (mom_units, [], None)),
+ ("particle_momentum_z", (mom_units, [], None)),
+ ("particle_angmomen_x", ("code_length**2/code_time", [], None)),
+ ("particle_angmomen_y", ("code_length**2/code_time", [], None)),
+ ("particle_angmomen_z", ("code_length**2/code_time", [], None)),
+ ("particle_id", ("", ["particle_index"], None)),
+ ("particle_mdot", ("code_mass/code_time", [], None)),
+ )
+
+The tuples, ``known_other_fields`` and ``known_particle_fields``
+contain entries, which are tuples of the form ``("name", ("units",
+["fields", "to", "alias"], "display_name"))``. ``"name"`` is the name
+of a field stored on-disk in the dataset. ``"units"`` corresponds to
+the units of that field. The list ``["fields", "to", "alias"]``
+allows you to specify additional aliases to this particular field; for
+example, if your on-disk field for the x-direction velocity were
+``"x-direction-velocity"``, maybe you'd prefer to alias to the more
+terse name of ``"xvel"``. ``"display_name"`` is an optional parameter
+that can be used to specify how you want the field to be displayed on
+a plot; this can be LaTeX code, for example the density field could
+have a display name of ``r"\rho"``. Omitting the ``"display_name"``
+will result in using a capitalized version of the ``"name"``.
Data Localization Structures
----------------------------
-As of right now, the "grid patch" mechanism is going to remain in yt, however in
-the future that may change. As such, some other output formats -- like Gadget --
-may be shoe-horned in, slightly.
+These functions and classes let yt know about how the arrangement of
+data on disk corresponds to the physical arrangement of data within
+the simulation. yt has grid datastructures for handling both
+patch-based and octree-based AMR codes. The terms 'patch-based'
+and 'octree-based' are used somewhat loosely here. For example,
+traditionally, the FLASH code used the paramesh AMR library, which is
+based on a tree structure, but the FLASH frontend in yt utilizes yt's
+patch-based datastructures. It is up to the frontend developer to
+determine which yt datastructures best match the datastructures of
+their simulation code.
-Hierarchy
-^^^^^^^^^
+Both approaches -- patch-based and octree-based -- have a concept of a
+*Hierarchy* or *Index* (used somewhat interchangeably in the code) of
+datastructures and something that describes the elements that make up
+the Hierarchy or Index. For patch-based codes, the Index is a
+collection of ``AMRGridPatch`` objects that describe a block of zones.
+For octree-based codes, the Index contains datastructures that hold
+information about the individual octs, namely an ``OctreeContainer``.
-To set up data localization, an ``AMRHierarchy`` subclass must be added in the
-file ``data_structures.py``. The index object must override the following
-methods:
+Hierarchy or Index
+^^^^^^^^^^^^^^^^^^
- * ``_detect_fields``: ``self.field_list`` must be populated as a list of
- strings corresponding to "native" fields in the data files.
- * ``_setup_classes``: it's probably safe to crib this from one of the other
- ``AMRHierarchy`` subclasses.
- * ``_count_grids``: this must set self.num_grids to be the total number of
- grids in the simulation.
- * ``_parse_index``: this must fill in ``grid_left_edge``,
+To set up data localization, a ``GridIndex`` subclass for patch-based
+codes or an ``OctreeIndex`` subclass for octree-based codes must be
+added in the file ``data_structures.py``. Examples of these different
+types of ``Index`` can be found in, for example, the
+``yt.frontends.chombo.data_structures.ChomboHierarchy`` for patch-based
+codes and ``yt.frontends.ramses.data_structures.RAMSESIndex`` for
+octree-based codes.
+
+For the most part, the ``GridIndex`` subclass must override (at a
+minimum) the following methods:
+
+ * ``_detect_output_fields()``: ``self.field_list`` must be populated as a list
+ of strings corresponding to "native" fields in the data files.
+ * ``_count_grids()``: this must set ``self.num_grids`` to be the total number
+ of grids (equivalently ``AMRGridPatch``'es) in the simulation.
+ * ``_parse_index()``: this must fill in ``grid_left_edge``,
``grid_right_edge``, ``grid_particle_count``, ``grid_dimensions`` and
- ``grid_levels`` with the appropriate information. Additionally, ``grids``
- must be an array of grid objects that already know their IDs.
- * ``_populate_grid_objects``: this initializes the grids by calling
- ``_prepare_grid`` and ``_setup_dx`` on all of them. Additionally, it should
- set up ``Children`` and ``Parent`` lists on each grid object.
- * ``_setup_unknown_fields``: If a field is in the data file that yt doesn't
- already know, this is where you make a guess at it.
- * ``_setup_derived_fields``: ``self.derived_field_list`` needs to be made a
- list of strings that correspond to all derived fields valid for this
- index.
+ ``grid_levels`` with the appropriate information. Each of these variables
+ is an array, with an entry for each of the ``self.num_grids`` grids.
+ Additionally, ``grids`` must be an array of ``AMRGridPatch`` objects that
+ already know their IDs.
+ * ``_populate_grid_objects()``: this initializes the grids by calling
+ ``_prepare_grid()`` and ``_setup_dx()`` on all of them. Additionally, it
+ should set up ``Children`` and ``Parent`` lists on each grid object.
-For the most part, the ``ChomboHierarchy`` should be the first place to look for
-hints on how to do this; ``EnzoHierarchy`` is also instructive.
+The ``OctreeIndex`` has somewhat analogous methods, but often with
+different names; both ``OctreeIndex`` and ``GridIndex`` are subclasses
+of the ``Index`` class. In particular, for the ``OctreeIndex``, the
+method ``_initialize_oct_handler()`` setups up much of the oct
+metadata that is analogous to the grid metadata created in the
+``GridIndex`` methods ``_count_grids()``, ``_parse_index()``, and
+``_populate_grid_objects()``.
Grids
^^^^^
-A new grid object, subclassing ``AMRGridPatch``, will also have to be added.
-This should go in ``data_structures.py``. For the most part, this may be all
+.. note:: This section only applies to the approach using yt's patch-based
+ datastructures. For the octree-based approach, one does not create
+ a grid object, but rather an ``OctreeSubset``, which has methods
+ for filling out portions of the octree structure. Again, see the
+ code in ``yt.frontends.ramses.data_structures`` for an example of
+ the octree approach.
+
+A new grid object, subclassing ``AMRGridPatch``, will also have to be added in
+``data_structures.py``. For the most part, this may be all
that is needed:
.. code-block:: python
@@ -115,32 +189,46 @@
self.Level = level
-Even the most complex grid object, ``OrionGrid``, is still relatively simple.
+Even one of the more complex grid objects,
+``yt.frontends.boxlib.BoxlibGrid``, is still relatively simple.
Data Reading Functions
----------------------
-In ``io.py``, there are a number of IO handlers that handle the mechanisms by
-which data is read off disk. To implement a new data reader, you must subclass
-``BaseIOHandler`` and override the following methods:
+In ``io.py``, there are a number of IO handlers that handle the
+mechanisms by which data is read off disk. To implement a new data
+reader, you must subclass ``BaseIOHandler``. The various frontend IO
+handlers are stored in an IO registry - essentially a dictionary that
+uses the name of the frontend as a key, and the specific IO handler as
+a value. It is important, therefore, to set the ``dataset_type``
+attribute of your subclass, which is what is used as the key in the IO
+registry. For example:
- * ``_read_field_names``: this routine accepts a grid object and must return all
- the fields in the data file affiliated with that grid. It is used at the
- initialization of the ``AMRHierarchy`` but likely not later.
- * ``modify``: This accepts a field from a data file and returns it ready to be
- used by yt. This is used in Enzo data for preloading.
- * ``_read_data_set``: This accepts a grid object and a field name and must
- return that field, ready to be used by yt as a NumPy array. Note that this
- presupposes that any actions done in ``modify`` (above) have been executed.
- * ``_read_data_slice``: This accepts a grid object, a field name, an axis and
- an (integer) coordinate, and it must return a slice through the array at that
- value.
- * ``preload``: (optional) This accepts a list of grids and a list of datasets
- and it populates ``self.queue`` (a dict keyed by grid id) with dicts of
- datasets.
- * ``_read_exception``: (property) This is a tuple of exceptions that can be
- raised by the data reading to indicate a field does not exist in the file.
+.. code-block:: python
+ class IOHandlerBoxlib(BaseIOHandler):
+ _dataset_type = "boxlib_native"
+ ...
+
+At a minimum, one should also override the following methods
+
+* ``_read_fluid_selection()``: this receives a collection of data "chunks", a
+ selector describing which "chunks" you are concerned with, a list of fields,
+ and the size of the data to read. It should create and return a dictionary
+ whose keys are the fields, and whose values are numpy arrays containing the
+ data. The data should actually be read via the ``_read_chunk_data()``
+ method.
+* ``_read_chunk_data()``: this method receives a "chunk" of data along with a
+ list of fields we want to read. It loops over all the grid objects within
+ the "chunk" of data and reads from disk the specific fields, returning a
+ dictionary whose keys are the fields and whose values are numpy arrays of
+ the data.
+
+If your dataset has particle information, you'll want to override the
+``_read_particle_coords()`` and ``read_particle_fields()`` methods as
+well. Each code is going to read data from disk in a different
+fashion, but the ``yt.frontends.boxlib.io.IOHandlerBoxlib`` is a
+decent place to start.
And that just about covers it. Please feel free to email
`yt-users <http://lists.spacepope.org/listinfo.cgi/yt-users-spacepope.org>`_ or
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 doc/source/examining/loading_data.rst
--- a/doc/source/examining/loading_data.rst
+++ b/doc/source/examining/loading_data.rst
@@ -983,6 +983,8 @@
onto the grid, you can also effectively mimic what your data would look like at
lower resolution.
+See :ref:`gadget-notebook` for an example.
+
.. _loading-tipsy-data:
Tipsy Data
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -469,6 +469,21 @@
slc.set_log('density', False)
slc.save()
+Specifically, a field containing both positive and negative values can be plotted
+with symlog scale, by seting the boolean to be ``True`` and providing an extra
+parameter ``linthresh``. In the region around zero (when the log scale approaches
+to infinity), the linear scale will be applied to the region ``(-linthresh, linthresh)``
+and stretched relative to the logarithmic range. You can also plot a positive field
+under symlog scale with the linear range of ``(0, linthresh)``.
+
+.. python-script::
+
+ import yt
+ ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030")
+ slc = yt.SlicePlot(ds, 'z', 'x-velocity', width=(30,'kpc'))
+ slc.set_log('x-velocity', True, linthresh=1.e1)
+ slc.save()
+
Lastly, the :meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.set_zlim`
function makes it possible to set a custom colormap range.
@@ -531,6 +546,26 @@
slc.set_buff_size(1600)
slc.save()
+Turning off minorticks
+~~~~~~~~~~~~~~~~~~~~~~
+
+By default minorticks for the x and y axes are turned on.
+The minorticks may be removed using the
+:meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.set_minorticks`
+function, which either accepts a specific field name including the 'all' alias
+and the desired state for the plot as 'on' or 'off'. There is also an analogous
+:meth:`~yt.visualization.plot_window.AxisAlignedSlicePlot.set_cbar_minorticks`
+function for the colorbar axis.
+
+.. python-script::
+
+ import yt
+ ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030")
+ slc = yt.SlicePlot(ds, 'z', 'density', width=(10,'kpc'))
+ slc.set_minorticks('all', 'off')
+ slc.set_cbar_minorticks('all', 'off')
+ slc.save()
+
.. _matplotlib-customization:
Further customization via matplotlib
@@ -743,7 +778,7 @@
Adjusting the plot units does not require recreating the histogram, so adjusting
units will always be inexpensive, requiring only an in-place unit conversion.
-In the following example we create a a plot of the average density in solar
+In the following example we create a plot of the average density in solar
masses per cubic parsec as a function of radius in kiloparsecs.
.. python-script::
@@ -892,7 +927,7 @@
``fractional`` keyword to ``True``. When set to ``True``, the value in each bin
is divided by the sum total from all bins. These can be turned into cumulative
distribution functions (CDFs) by setting the ``accumulation`` keyword to
-``True``. This will make is so that the value in any bin N is the cumulative
+``True``. This will make it so that the value in any bin N is the cumulative
sum of all bins from 0 to N. The direction of the summation can be reversed by
setting ``accumulation`` to ``-True``. For ``PhasePlot``, the accumulation can
be set independently for each axis by setting ``accumulation`` to a list of
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 setup.py
--- a/setup.py
+++ b/setup.py
@@ -6,6 +6,12 @@
import subprocess
import shutil
import glob
+
+if sys.version_info < (2, 7):
+ print("yt currently requires Python version 2.7")
+ print("certain features may fail unexpectedly and silently with older versions.")
+ sys.exit(1)
+
import setuptools
from distutils.version import StrictVersion
if StrictVersion(setuptools.__version__) < StrictVersion('0.7.0'):
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/analysis_modules/cosmological_observation/light_cone/light_cone.py
--- a/yt/analysis_modules/cosmological_observation/light_cone/light_cone.py
+++ b/yt/analysis_modules/cosmological_observation/light_cone/light_cone.py
@@ -186,13 +186,13 @@
# Simple error check to make sure more than 100% of box depth
# is never required.
if self.light_cone_solution[q]["box_depth_fraction"] > 1.0:
- mylog.debug(("Warning: box fraction required to go from " +
+ mylog.error(("Warning: box fraction required to go from " +
"z = %f to %f is %f") %
(self.light_cone_solution[q]["redshift"], z_next,
self.light_cone_solution[q]["box_depth_fraction"]))
- mylog.debug(("Full box delta z is %f, but it is %f to the " +
+ mylog.error(("Full box delta z is %f, but it is %f to the " +
"next data dump.") %
- (self.light_cone_solution[q]["deltazMax"],
+ (self.light_cone_solution[q]["dz_max"],
self.light_cone_solution[q]["redshift"]-z_next))
# Get projection axis and center.
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/analysis_modules/cosmological_observation/light_ray/light_ray.py
--- a/yt/analysis_modules/cosmological_observation/light_ray/light_ray.py
+++ b/yt/analysis_modules/cosmological_observation/light_ray/light_ray.py
@@ -169,7 +169,7 @@
(self.light_ray_solution[q]['redshift'], z_next,
self.light_ray_solution[q]['traversal_box_fraction']))
mylog.error("Full box delta z is %f, but it is %f to the next data dump." %
- (self.light_ray_solution[q]['deltazMax'],
+ (self.light_ray_solution[q]['dz_max'],
self.light_ray_solution[q]['redshift']-z_next))
# Get dataset axis and center.
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/analysis_modules/photon_simulator/photon_simulator.py
--- a/yt/analysis_modules/photon_simulator/photon_simulator.py
+++ b/yt/analysis_modules/photon_simulator/photon_simulator.py
@@ -729,6 +729,7 @@
events[tblhdu.header["CHANTYPE"]] = dchannel.astype(int)
info = {"ChannelType" : tblhdu.header["CHANTYPE"],
+ "Mission" : tblhdu.header["MISSION"],
"Telescope" : tblhdu.header["TELESCOP"],
"Instrument" : tblhdu.header["INSTRUME"]}
@@ -789,6 +790,8 @@
parameters["ARF"] = f["/arf"].value
if "channel_type" in f:
parameters["ChannelType"] = f["/channel_type"].value
+ if "mission" in f:
+ parameters["Mission"] = f["/mission"].value
if "telescope" in f:
parameters["Telescope"] = f["/telescope"].value
if "instrument" in f:
@@ -831,6 +834,8 @@
parameters["ARF"] = tblhdu["ARF"]
if "CHANTYPE" in tblhdu.header:
parameters["ChannelType"] = tblhdu["CHANTYPE"]
+ if "MISSION" in tblhdu.header:
+ parameters["Mission"] = tblhdu["MISSION"]
if "TELESCOP" in tblhdu.header:
parameters["Telescope"] = tblhdu["TELESCOP"]
if "INSTRUME" in tblhdu.header:
@@ -920,11 +925,13 @@
tbhdu.header["RADECSYS"] = "FK5"
tbhdu.header["EQUINOX"] = 2000.0
if "RMF" in self.parameters:
- tbhdu.header["RMF"] = self.parameters["RMF"]
+ tbhdu.header["RESPFILE"] = self.parameters["RMF"]
if "ARF" in self.parameters:
- tbhdu.header["ARF"] = self.parameters["ARF"]
+ tbhdu.header["ANCRFILE"] = self.parameters["ARF"]
if "ChannelType" in self.parameters:
tbhdu.header["CHANTYPE"] = self.parameters["ChannelType"]
+ if "Mission" in self.parameters:
+ tbhdu.header["MISSION"] = self.parameters["Mission"]
if "Telescope" in self.parameters:
tbhdu.header["TELESCOP"] = self.parameters["Telescope"]
if "Instrument" in self.parameters:
@@ -1041,6 +1048,8 @@
f.create_dataset("/rmf", data=self.parameters["RMF"])
if "ChannelType" in self.parameters:
f.create_dataset("/channel_type", data=self.parameters["ChannelType"])
+ if "Mission" in self.parameters:
+ f.create_dataset("/mission", data=self.parameters["Mission"])
if "Telescope" in self.parameters:
f.create_dataset("/telescope", data=self.parameters["Telescope"])
if "Instrument" in self.parameters:
@@ -1209,6 +1218,10 @@
tbhdu.header["ANCRFILE"] = self.parameters["ARF"]
else:
tbhdu.header["ANCRFILE"] = "none"
+ if self.parameters.has_key("Mission"):
+ tbhdu.header["MISSION"] = self.parameters["Mission"]
+ else:
+ tbhdu.header["MISSION"] = "none"
if self.parameters.has_key("Telescope"):
tbhdu.header["TELESCOP"] = self.parameters["Telescope"]
else:
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/data_objects/data_containers.py
--- a/yt/data_objects/data_containers.py
+++ b/yt/data_objects/data_containers.py
@@ -807,6 +807,8 @@
self.fields = [k for k in self.field_data if k not in skip]
if fields is not None:
self.fields = ensure_list(fields) + self.fields
+ if len(self.fields) == 0:
+ raise ValueError("No fields found to plot in get_pw")
(bounds, center, display_center) = \
get_window_parameters(axis, center, width, self.ds)
pw = PWViewerMPL(self, bounds, fields=self.fields, origin=origin,
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/data_objects/selection_data_containers.py
--- a/yt/data_objects/selection_data_containers.py
+++ b/yt/data_objects/selection_data_containers.py
@@ -333,61 +333,6 @@
def normal(self):
return self._norm_vec
- def to_frb(self, width, resolution, height=None,
- periodic=False):
- r"""This function returns an ObliqueFixedResolutionBuffer generated
- from this object.
-
- An ObliqueFixedResolutionBuffer is an object that accepts a
- variable-resolution 2D object and transforms it into an NxM bitmap that
- can be plotted, examined or processed. This is a convenience function
- to return an FRB directly from an existing 2D data object. Unlike the
- corresponding to_frb function for other YTSelectionContainer2D objects,
- this does not accept a 'center' parameter as it is assumed to be
- centered at the center of the cutting plane.
-
- Parameters
- ----------
- width : width specifier
- This can either be a floating point value, in the native domain
- units of the simulation, or a tuple of the (value, unit) style.
- This will be the width of the FRB.
- height : height specifier, optional
- This will be the height of the FRB, by default it is equal to width.
- resolution : int or tuple of ints
- The number of pixels on a side of the final FRB.
-
- Returns
- -------
- frb : :class:`~yt.visualization.fixed_resolution.ObliqueFixedResolutionBuffer`
- A fixed resolution buffer, which can be queried for fields.
-
- Examples
- --------
-
- >>> v, c = ds.find_max("density")
- >>> sp = ds.sphere(c, (100.0, 'au'))
- >>> L = sp.quantities.angular_momentum_vector()
- >>> cutting = ds.cutting(L, c)
- >>> frb = cutting.to_frb( (1.0, 'pc'), 1024)
- >>> write_image(np.log10(frb["Density"]), 'density_1pc.png')
- """
- if iterable(width):
- w, u = width
- width = self.ds.quan(w, input_units = u)
- if height is None:
- height = width
- elif iterable(height):
- h, u = height
- height = self.ds.quan(w, input_units = u)
- if not iterable(resolution):
- resolution = (resolution, resolution)
- from yt.visualization.fixed_resolution import ObliqueFixedResolutionBuffer
- bounds = (-width/2.0, width/2.0, -height/2.0, height/2.0)
- frb = ObliqueFixedResolutionBuffer(self, bounds, resolution,
- periodic = periodic)
- return frb
-
def _generate_container_field(self, field):
if self._current_chunk is None:
self.index._identify_base_chunk(self)
@@ -455,7 +400,7 @@
pw._setup_plots()
return pw
- def to_frb(self, width, resolution, height=None):
+ def to_frb(self, width, resolution, height=None, periodic=False):
r"""This function returns an ObliqueFixedResolutionBuffer generated
from this object.
@@ -477,6 +422,9 @@
This will be the height of the FRB, by default it is equal to width.
resolution : int or tuple of ints
The number of pixels on a side of the final FRB.
+ periodic : boolean
+ This can be true or false, and governs whether the pixelization
+ will span the domain boundaries.
Returns
-------
@@ -505,7 +453,8 @@
resolution = (resolution, resolution)
from yt.visualization.fixed_resolution import ObliqueFixedResolutionBuffer
bounds = (-width/2.0, width/2.0, -height/2.0, height/2.0)
- frb = ObliqueFixedResolutionBuffer(self, bounds, resolution)
+ frb = ObliqueFixedResolutionBuffer(self, bounds, resolution,
+ periodic=periodic)
return frb
class YTDiskBase(YTSelectionContainer3D):
@@ -593,7 +542,7 @@
"""
_type_name = "data_collection"
_con_args = ("_obj_list",)
- def __init__(self, center, obj_list, ds = None, field_parameters = None):
+ def __init__(self, obj_list, ds=None, field_parameters=None, center=None):
YTSelectionContainer3D.__init__(self, center, ds, field_parameters)
self._obj_ids = np.array([o.id - o._id_offset for o in obj_list],
dtype="int64")
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/data_objects/static_output.py
--- a/yt/data_objects/static_output.py
+++ b/yt/data_objects/static_output.py
@@ -328,10 +328,10 @@
mylog.debug("Creating Particle Union 'all'")
pu = ParticleUnion("all", list(self.particle_types_raw))
self.add_particle_union(pu)
+ mylog.info("Loading field plugins.")
+ self.field_info.load_all_plugins()
deps, unloaded = self.field_info.check_derived_fields()
self.field_dependencies.update(deps)
- mylog.info("Loading field plugins.")
- self.field_info.load_all_plugins()
def setup_deprecated_fields(self):
from yt.fields.field_aliases import _field_name_aliases
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/data_objects/tests/test_data_collection.py
--- a/yt/data_objects/tests/test_data_collection.py
+++ b/yt/data_objects/tests/test_data_collection.py
@@ -8,7 +8,7 @@
# We decompose in different ways
for nprocs in [1, 2, 4, 8]:
ds = fake_random_ds(16, nprocs = nprocs)
- coll = ds.data_collection(ds.domain_center, ds.index.grids)
+ coll = ds.data_collection(ds.index.grids)
crho = coll["density"].sum(dtype="float64").to_ndarray()
grho = np.sum([g["density"].sum(dtype="float64") for g in ds.index.grids],
dtype="float64")
@@ -16,7 +16,7 @@
yield assert_equal, coll.size, ds.domain_dimensions.prod()
for gi in range(ds.index.num_grids):
grids = ds.index.grids[:gi+1]
- coll = ds.data_collection(ds.domain_center, grids)
+ coll = ds.data_collection(grids)
crho = coll["density"].sum(dtype="float64")
grho = np.sum([g["density"].sum(dtype="float64") for g in grids],
dtype="float64")
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/fields/species_fields.py
--- a/yt/fields/species_fields.py
+++ b/yt/fields/species_fields.py
@@ -178,3 +178,4 @@
# Skip it
continue
func(registry, ftype, species, particle_type)
+ add_nuclei_density_fields(registry, ftype, particle_type=particle_type)
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/frontends/_skeleton/api.py
--- a/yt/frontends/_skeleton/api.py
+++ b/yt/frontends/_skeleton/api.py
@@ -19,8 +19,7 @@
SkeletonDataset
from .fields import \
- SkeletonFieldInfo, \
- add_skeleton_field
+ SkeletonFieldInfo
from .io import \
IOHandlerSkeleton
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/frontends/_skeleton/data_structures.py
--- a/yt/frontends/_skeleton/data_structures.py
+++ b/yt/frontends/_skeleton/data_structures.py
@@ -13,18 +13,13 @@
# The full license is in the file COPYING.txt, distributed with this software.
#-----------------------------------------------------------------------------
-import numpy as np
-
-from yt.data_objects.grid_patch import \
- AMRGridPatch
from yt.data_objects.grid_patch import \
AMRGridPatch
from yt.geometry.grid_geometry_handler import \
GridIndex
from yt.data_objects.static_output import \
Dataset
-from yt.utilities.lib.misc_utilities import \
- get_box_grids_level
+from .fields import SkeletonFieldInfo
class SkeletonGrid(AMRGridPatch):
_id_offset = 0
@@ -41,20 +36,15 @@
def __repr__(self):
return "SkeletonGrid_%04i (%s)" % (self.id, self.ActiveDimensions)
-class SkeletonHierarchy(AMRHierarchy):
-
+class SkeletonHierarchy(GridIndex):
grid = SkeletonGrid
def __init__(self, ds, dataset_type='skeleton'):
self.dataset_type = dataset_type
- self.dataset = weakref.proxy(ds)
# for now, the index file is the dataset!
self.index_filename = self.dataset.parameter_filename
self.directory = os.path.dirname(self.index_filename)
- AMRHierarchy.__init__(self, ds, dataset_type)
-
- def _initialize_data_storage(self):
- pass
+ GridIndex.__init__(self, ds, dataset_type)
def _detect_output_fields(self):
# This needs to set a self.field_list that contains all the available,
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/frontends/_skeleton/io.py
--- a/yt/frontends/_skeleton/io.py
+++ b/yt/frontends/_skeleton/io.py
@@ -13,9 +13,6 @@
# The full license is in the file COPYING.txt, distributed with this software.
#-----------------------------------------------------------------------------
-import numpy as np
-import h5py
-
from yt.utilities.io_handler import \
BaseIOHandler
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/frontends/chombo/data_structures.py
--- a/yt/frontends/chombo/data_structures.py
+++ b/yt/frontends/chombo/data_structures.py
@@ -113,7 +113,8 @@
self.directory = ds.fullpath
self._handle = ds._handle
- self.float_type = self._handle['Chombo_global'].attrs['testReal'].dtype.name
+ tr = self._handle['Chombo_global'].attrs.get("testReal", "float32")
+
self._levels = [key for key in self._handle.keys() if key.startswith('level')]
GridIndex.__init__(self, ds, dataset_type)
@@ -156,12 +157,18 @@
for key, val in self._handle.attrs.items():
if key.startswith("particle"):
particle_fields.append(val)
- self.field_list.extend([("io", c) for c in particle_fields])
+ self.field_list.extend([("io", c) for c in particle_fields])
def _count_grids(self):
self.num_grids = 0
for lev in self._levels:
- self.num_grids += self._handle[lev]['Processors'].len()
+ d = self._handle[lev]
+ if 'Processors' in d:
+ self.num_grids += d['Processors'].len()
+ elif 'boxes' in d:
+ self.num_grids += d['boxes'].len()
+ else:
+ raise RuntimeError("Uknown file specification")
def _parse_index(self):
f = self._handle # shortcut
@@ -534,7 +541,8 @@
# look for particle fields
self.particle_filename = self.index_filename[:-4] + 'sink'
- if not os.path.exists(self.particle_filename): return
+ if not os.path.exists(self.particle_filename):
+ return
pfield_list = [("io", c) for c in self.io.particle_field_index.keys()]
self.field_list.extend(pfield_list)
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/frontends/chombo/io.py
--- a/yt/frontends/chombo/io.py
+++ b/yt/frontends/chombo/io.py
@@ -25,6 +25,7 @@
_dataset_type = "chombo_hdf5"
_offset_string = 'data:offsets=0'
_data_string = 'data:datatype=0'
+ _offsets = None
def __init__(self, ds, *args, **kwargs):
BaseIOHandler.__init__(self, ds, *args, **kwargs)
@@ -32,6 +33,29 @@
self._handle = ds._handle
self.dim = self._handle['Chombo_global/'].attrs['SpaceDim']
self._read_ghost_info()
+ if self._offset_string not in self._handle['level_0']:
+ self._calculate_offsets()
+
+ def _calculate_offsets(self):
+ def box_size(corners):
+ size = 1
+ for idim in range(self.dim):
+ size *= (corners[idim+self.dim] - corners[idim] + 1)
+ return size
+
+ self._offsets = {}
+ num_comp = self._handle.attrs['num_components']
+ level = 0
+ while 1:
+ lname = 'level_%i' % level
+ if lname not in self._handle: break
+ boxes = self._handle['level_0']['boxes'].value
+ box_sizes = np.array([box_size(box) for box in boxes])
+
+ offsets = np.cumsum(box_sizes*num_comp, dtype='int64')
+ offsets -= offsets[0]
+ self._offsets[level] = offsets
+ level += 1
def _read_ghost_info(self):
try:
@@ -41,7 +65,7 @@
self.ghost = np.array(self.ghost)
except KeyError:
# assume zero ghosts if outputGhosts not present
- self.ghost = np.zeros(self.dim)
+ self.ghost = np.zeros(self.dim, 'int64')
_field_dict = None
@property
@@ -80,7 +104,10 @@
shape = grid.ActiveDimensions + 2*self.ghost
boxsize = shape.prod()
- grid_offset = lev[self._offset_string][grid._level_id]
+ if self._offsets is not None:
+ grid_offset = self._offsets[grid.Level][grid._level_id]
+ else:
+ grid_offset = lev[self._offset_string][grid._level_id]
start = grid_offset+self.field_dict[field]*boxsize
stop = start + boxsize
data = lev[self._data_string][start:stop]
@@ -223,7 +250,14 @@
# Figure out the format of the particle file
with open(fn, 'r') as f:
lines = f.readlines()
- line = lines[1]
+
+ try:
+ line = lines[1]
+ except IndexError:
+ # a particle file exists, but there is only one line,
+ # so no sinks have been created yet.
+ index = {}
+ return index
# The basic fields that all sink particles have
index = {'particle_mass': 0,
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/frontends/enzo/fields.py
--- a/yt/frontends/enzo/fields.py
+++ b/yt/frontends/enzo/fields.py
@@ -20,9 +20,6 @@
FieldInfoContainer
from yt.units.yt_array import \
YTArray
-from yt.fields.species_fields import \
- add_nuclei_density_fields, \
- add_species_field_by_density
from yt.utilities.physical_constants import \
mh, me, mp, \
mass_sun_cgs
@@ -152,7 +149,6 @@
for sp in species_names:
self.add_species_field(sp)
self.species_names.append(known_species_names[sp])
- add_nuclei_density_fields(self, "gas")
def setup_fluid_fields(self):
# Now we conditionally load a few other things.
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/frontends/enzo/tests/test_outputs.py
--- a/yt/frontends/enzo/tests/test_outputs.py
+++ b/yt/frontends/enzo/tests/test_outputs.py
@@ -90,3 +90,13 @@
ds = data_dir_load(ecp)
# Now we test our species fields
yield check_color_conservation(ds)
+
+ at requires_ds(ecp, big_data=True)
+def test_nuclei_density_fields():
+ ds = data_dir_load(ecp)
+ ad = ds.all_data()
+ yield assert_array_equal, ad["H_nuclei_density"], \
+ (ad["H_number_density"] + ad["H_p1_number_density"])
+ yield assert_array_equal, ad["He_nuclei_density"], \
+ (ad["He_number_density"] + ad["He_p1_number_density"] +
+ ad["He_p2_number_density"])
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/frontends/fits/data_structures.py
--- a/yt/frontends/fits/data_structures.py
+++ b/yt/frontends/fits/data_structures.py
@@ -157,6 +157,8 @@
naxis4 = 1
for i, fits_file in enumerate(self.dataset._handle._fits_files):
for j, hdu in enumerate(fits_file):
+ if isinstance(hdu, _astropy.pyfits.BinTableHDU):
+ continue
if self._ensure_same_dims(hdu):
units = self._determine_image_units(hdu.header, known_units)
try:
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/frontends/halo_catalogs/owls_subfind/data_structures.py
--- a/yt/frontends/halo_catalogs/owls_subfind/data_structures.py
+++ b/yt/frontends/halo_catalogs/owls_subfind/data_structures.py
@@ -80,7 +80,7 @@
# TODO: Add additional fields
dsl = []
units = {}
- for dom in self.data_files[:1]:
+ for dom in self.data_files:
fl, _units = self.io._identify_fields(dom)
units.update(_units)
dom._calculate_offsets(fl)
@@ -208,14 +208,15 @@
@classmethod
def _is_valid(self, *args, **kwargs):
+ need_groups = ['Constants', 'Header', 'Parameters', 'Units', 'FOF']
+ veto_groups = []
+ valid = True
try:
- fileh = h5py.File(args[0], mode='r')
- if "Constants" in fileh["/"].keys() and \
- "Header" in fileh["/"].keys() and \
- "SUBFIND" in fileh["/"].keys():
- fileh.close()
- return True
- fileh.close()
+ fh = h5py.File(args[0], mode='r')
+ valid = all(ng in fh["/"] for ng in need_groups) and \
+ not any(vg in fh["/"] for vg in veto_groups)
+ fh.close()
except:
+ valid = False
pass
- return False
+ return valid
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/frontends/halo_catalogs/owls_subfind/io.py
--- a/yt/frontends/halo_catalogs/owls_subfind/io.py
+++ b/yt/frontends/halo_catalogs/owls_subfind/io.py
@@ -82,12 +82,13 @@
with h5py.File(data_file.filename, "r") as f:
for ptype, field_list in sorted(ptf.items()):
pcount = data_file.total_particles[ptype]
+ if pcount == 0: continue
coords = f[ptype]["CenterOfMass"].value.astype("float64")
coords = np.resize(coords, (pcount, 3))
x = coords[:, 0]
y = coords[:, 1]
z = coords[:, 2]
- mask = selector.select_points(x, y, z)
+ mask = selector.select_points(x, y, z, 0.0)
del x, y, z
if mask is None: continue
for field in field_list:
@@ -113,8 +114,9 @@
yield (ptype, field), data
def _initialize_index(self, data_file, regions):
- pcount = sum(self._count_particles(data_file).values())
+ pcount = sum(data_file.total_particles.values())
morton = np.empty(pcount, dtype='uint64')
+ if pcount == 0: return morton
mylog.debug("Initializing index % 5i (% 7i particles)",
data_file.file_id, pcount)
ind = 0
@@ -122,12 +124,11 @@
if not f.keys(): return None
dx = np.finfo(f["FOF"]['CenterOfMass'].dtype).eps
dx = 2.0*self.ds.quan(dx, "code_length")
-
- for ptype, pattr in zip(["FOF", "SUBFIND"],
- ["Number_of_groups", "Number_of_subgroups"]):
- my_pcount = f[ptype].attrs[pattr]
+
+ for ptype in data_file.ds.particle_types_raw:
+ if data_file.total_particles[ptype] == 0: continue
pos = f[ptype]["CenterOfMass"].value.astype("float64")
- pos = np.resize(pos, (my_pcount, 3))
+ pos = np.resize(pos, (data_file.total_particles[ptype], 3))
pos = data_file.ds.arr(pos, "code_length")
# These are 32 bit numbers, so we give a little lee-way.
@@ -151,17 +152,24 @@
def _count_particles(self, data_file):
with h5py.File(data_file.filename, "r") as f:
- # We need this to figure out where the offset fields are stored.
- data_file.total_offset = f["SUBFIND"].attrs["Number_of_groups"]
- return {"FOF": f["FOF"].attrs["Number_of_groups"],
- "SUBFIND": f["FOF"].attrs["Number_of_subgroups"]}
+ pcount = {"FOF": f["FOF"].attrs["Number_of_groups"]}
+ if "SUBFIND" in f:
+ # We need this to figure out where the offset fields are stored.
+ data_file.total_offset = f["SUBFIND"].attrs["Number_of_groups"]
+ pcount["SUBFIND"] = f["FOF"].attrs["Number_of_subgroups"]
+ else:
+ data_file.total_offset = 0
+ pcount["SUBFIND"] = 0
+ return pcount
def _identify_fields(self, data_file):
- fields = [(ptype, "particle_identifier")
- for ptype in self.ds.particle_types_raw]
+ fields = []
pcount = data_file.total_particles
+ if sum(pcount.values()) == 0: return fields, {}
with h5py.File(data_file.filename, "r") as f:
for ptype in self.ds.particle_types_raw:
+ if data_file.total_particles[ptype] == 0: continue
+ fields.append((ptype, "particle_identifier"))
my_fields, my_offset_fields = \
subfind_field_list(f[ptype], ptype, data_file.total_particles)
fields.extend(my_fields)
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/frontends/halo_catalogs/owls_subfind/tests/test_outputs.py
--- /dev/null
+++ b/yt/frontends/halo_catalogs/owls_subfind/tests/test_outputs.py
@@ -0,0 +1,40 @@
+"""
+OWLSSubfind frontend tests using owls_fof_halos datasets
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2013, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+from yt.testing import *
+from yt.utilities.answer_testing.framework import \
+ FieldValuesTest, \
+ requires_ds, \
+ data_dir_load
+
+_fields = ("particle_position_x", "particle_position_y",
+ "particle_position_z", "particle_mass")
+
+g8 = "owls_fof_halos/groups_008/group_008.0.hdf5"
+ at requires_ds(g8)
+def test_fields_g8():
+ ds = data_dir_load(g8)
+ yield assert_equal, str(ds), "group_008.0.hdf5"
+ for field in _fields:
+ yield FieldValuesTest(g8, field)
+
+# a dataset with empty files
+g3 = "owls_fof_halos/groups_003/group_003.0.hdf5"
+ at requires_ds(g3)
+def test_fields_g3():
+ ds = data_dir_load(g3)
+ yield assert_equal, str(ds), "group_003.0.hdf5"
+ for field in _fields:
+ yield FieldValuesTest(g3, field)
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/frontends/sph/data_structures.py
--- a/yt/frontends/sph/data_structures.py
+++ b/yt/frontends/sph/data_structures.py
@@ -379,7 +379,7 @@
@classmethod
def _is_valid(self, *args, **kwargs):
need_groups = ['Constants', 'Header', 'Parameters', 'Units']
- veto_groups = ['SUBFIND',
+ veto_groups = ['SUBFIND', 'FOF',
'PartType0/ChemistryAbundances',
'PartType0/ChemicalAbundances',
'RuntimePars', 'HashTable']
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/frontends/stream/data_structures.py
--- a/yt/frontends/stream/data_structures.py
+++ b/yt/frontends/stream/data_structures.py
@@ -364,8 +364,10 @@
_additional_fields = ()
@property
- def all_fields(self):
- fields = list(self._additional_fields) + self[0].keys()
+ def all_fields(self):
+ self_fields = chain.from_iterable(s.keys() for s in self.values())
+ self_fields = list(set(self_fields))
+ fields = list(self._additional_fields) + self_fields
fields = list(set(fields))
return fields
@@ -471,9 +473,8 @@
field_units[k] = v.units
new_data[k] = v.copy().d
data = new_data
- elif all([isinstance(val, np.ndarray) for val in data.values()]):
- field_units = {field:'' for field in data.keys()}
- elif all([(len(val) == 2) for val in data.values()]):
+ elif all([((not isinstance(val, np.ndarray)) and (len(val) == 2))
+ for val in data.values()]):
new_data, field_units = {}, {}
for field in data:
try:
@@ -489,6 +490,9 @@
raise RuntimeError("The data dict appears to be invalid.\n" +
str(e))
data = new_data
+ elif all([iterable(val) for val in data.values()]):
+ field_units = {field:'' for field in data.keys()}
+ data = dict((field, np.array(val)) for field, val in data.iteritems())
else:
raise RuntimeError("The data dict appears to be invalid. "
"The data dictionary must map from field "
@@ -697,7 +701,7 @@
field_units=None, bbox=None, sim_time=0.0, length_unit=None,
mass_unit=None, time_unit=None, velocity_unit=None,
magnetic_unit=None, periodicity=(True, True, True),
- geometry = "cartesian"):
+ geometry = "cartesian", refine_by=2):
r"""Load a set of grids of data into yt as a
:class:`~yt.frontends.stream.data_structures.StreamHandler`.
This should allow a sequence of grids of varying resolution of data to be
@@ -747,6 +751,8 @@
each axis
geometry : string
"cartesian", "cylindrical" or "polar"
+ refine_by : integer
+ Specifies the refinement ratio between levels. Defaults to 2.
Examples
--------
@@ -763,12 +769,13 @@
... dimensions = [32, 32, 32],
... number_of_particles = 0)
... ]
- ...
+ ...
>>> for g in grid_data:
- ... g["Density"] = np.random.random(g["dimensions"]) * 2**g["level"]
+ ... g["density"] = np.random.random(g["dimensions"]) * 2**g["level"]
...
- >>> units = dict(Density='g/cm**3')
- >>> ds = load_amr_grids(grid_data, [32, 32, 32], 1.0)
+ >>> units = dict(density='g/cm**3')
+ >>> ds = load_amr_grids(grid_data, [32, 32, 32], field_units=units,
+ ... length_unit=1.0)
"""
domain_dimensions = np.array(domain_dimensions)
@@ -822,6 +829,11 @@
if magnetic_unit is None:
magnetic_unit = 'code_magnetic'
+ particle_types = {}
+
+ for grid in grid_data:
+ particle_types.update(set_particle_types(grid))
+
handler = StreamHandler(
grid_left_edges,
grid_right_edges,
@@ -833,13 +845,13 @@
sfh,
field_units,
(length_unit, mass_unit, time_unit, velocity_unit, magnetic_unit),
- particle_types=set_particle_types(grid_data[0])
+ particle_types=particle_types
)
handler.name = "AMRGridData"
handler.domain_left_edge = domain_left_edge
handler.domain_right_edge = domain_right_edge
- handler.refine_by = 2
+ handler.refine_by = refine_by
handler.dimensionality = 3
handler.domain_dimensions = domain_dimensions
handler.simulation_time = sim_time
@@ -1018,7 +1030,7 @@
magnetic_unit : float
Conversion factor from simulation magnetic units to gauss
bbox : array_like (xdim:zdim, LE:RE), optional
- Size of computational domain in units sim_unit_to_cm
+ Size of computational domain in units of the length_unit
sim_time : float, optional
The simulation time in seconds
periodicity : tuple of booleans
@@ -1191,10 +1203,8 @@
coordinates : array_like
This should be of size (M,3) where M is the number of vertices
indicated in the connectivity matrix.
- sim_unit_to_cm : float
- Conversion factor from simulation units to centimeters
bbox : array_like (xdim:zdim, LE:RE), optional
- Size of computational domain in units sim_unit_to_cm
+ Size of computational domain in units of the length unit.
sim_time : float, optional
The simulation time in seconds
mass_unit : string
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/frontends/stream/tests/test_stream_amrgrids.py
--- a/yt/frontends/stream/tests/test_stream_amrgrids.py
+++ b/yt/frontends/stream/tests/test_stream_amrgrids.py
@@ -26,3 +26,25 @@
p = ProjectionPlot(spf, 'x', ["density"], center='c', origin='native')
return p
yield assert_raises, YTIntDomainOverflow, make_proj
+
+def test_refine_by():
+ grid_data = []
+ ref_by = 4
+ lo = 0.0
+ hi = 1.0
+ fine_grid_width = (hi - lo) / ref_by
+ for level in range(2):
+ grid_dict = {}
+
+ grid_dict['left_edge'] = [0.0 + 0.5*fine_grid_width*level]*3
+ grid_dict['right_edge'] = [1.0 - 0.5*fine_grid_width*level]*3
+ grid_dict['dimensions'] = [8, 8, 8]
+ grid_dict['level'] = level
+
+ grid_dict['density'] = np.ones((8,8,8))
+
+ grid_data.append(grid_dict)
+
+ domain_dimensions = np.array([8, 8, 8])
+
+ spf = load_amr_grids(grid_data, domain_dimensions, refine_by=ref_by)
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/geometry/selection_routines.pyx
--- a/yt/geometry/selection_routines.pyx
+++ b/yt/geometry/selection_routines.pyx
@@ -727,9 +727,14 @@
if dobj.left_edge[i] < dobj.ds.domain_left_edge[i] or \
dobj.right_edge[i] > dobj.ds.domain_right_edge[i]:
raise RuntimeError(
- "Error: bad Region in non-periodic domain along dimension %s. "
- "Region left edge = %s, Region right edge = %s"
- "Dataset left edge = %s, Dataset right edge = %s" % \
+ "Error: yt attempted to read outside the boundaries of "
+ "a non-periodic domain along dimension %s.\n"
+ "Region left edge = %s, Region right edge = %s\n"
+ "Dataset left edge = %s, Dataset right edge = %s\n\n"
+ "This commonly happens when trying to compute ghost cells "
+ "up to the domain boundary. Two possible solutions are to "
+ "load a smaller region that does not border the edge or "
+ "override the periodicity for this dataset." % \
(i, dobj.left_edge[i], dobj.right_edge[i],
dobj.ds.domain_left_edge[i], dobj.ds.domain_right_edge[i])
)
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/units/yt_array.py
--- a/yt/units/yt_array.py
+++ b/yt/units/yt_array.py
@@ -44,29 +44,6 @@
except: return False
return True
-def ensure_unitless(func):
- @wraps(func)
- def wrapped(unit):
- if unit != Unit():
- raise RuntimeError(
- "This operation is only defined for unitless quantities. "
- "Received unit (%s)" % unit
- )
- return func(unit)
- return wrapped
-
-def ensure_same_dimensions(func):
- @wraps(func)
- def wrapped(unit1, unit2):
- if unit1 is None and not unit2.is_dimensionless:
- raise RuntimeError
- elif unit2 is None and not unit1.is_dimensionless:
- raise RuntimeError
- elif unit1.dimensions != unit2.dimensions:
- raise RuntimeError
- return func(unit1, unit2)
- return wrapped
-
def return_arr(func):
@wraps(func)
def wrapped(*args, **kwargs):
@@ -84,7 +61,6 @@
def multiply_units(unit1, unit2):
return unit1 * unit2
- at ensure_same_dimensions
def preserve_units(unit1, unit2):
return unit1
@@ -106,15 +82,9 @@
def return_without_unit(unit):
return None
- at ensure_unitless
-def unitless(unit):
- return Unit()
-
- at ensure_same_dimensions
def arctan2_unit(unit1, unit2):
return Unit()
- at ensure_same_dimensions
def comparison_unit(unit1, unit2):
return None
@@ -979,12 +949,7 @@
u = getattr(context[1][0], 'units', None)
if u is None:
u = Unit()
- try:
- unit = self._ufunc_registry[context[0]](u)
- # Catch the RuntimeError raised inside of ensure_same_dimensions
- # Raise YTUnitOperationError up here since we know the context now
- except RuntimeError:
- raise YTUnitOperationError(context[0], u)
+ unit = self._ufunc_registry[context[0]](u)
ret_class = type(self)
elif context[0] in binary_operators:
oper1 = coerce_iterable_units(context[1][0])
@@ -1014,12 +979,7 @@
raise YTUnitOperationError(context[0], unit1, unit2)
else:
raise YTUfuncUnitError(context[0], unit1, unit2)
- try:
- unit = self._ufunc_registry[context[0]](unit1, unit2)
- # Catch the RuntimeError raised inside of ensure_same_dimensions
- # Raise YTUnitOperationError up here since we know the context now
- except RuntimeError:
- raise YTUnitOperationError(context[0], unit1, unit2)
+ unit = self._ufunc_registry[context[0]](unit1, unit2)
else:
raise RuntimeError("Operation is not defined.")
if unit is None:
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/visualization/base_plot_types.py
--- a/yt/visualization/base_plot_types.py
+++ b/yt/visualization/base_plot_types.py
@@ -109,12 +109,16 @@
cax.set_position(caxrect)
self.cax = cax
- def _init_image(self, data, cbnorm, cmap, extent, aspect):
+ def _init_image(self, data, cbnorm, cblinthresh, cmap, extent, aspect):
"""Store output of imshow in image variable"""
if (cbnorm == 'log10'):
norm = matplotlib.colors.LogNorm()
elif (cbnorm == 'linear'):
norm = matplotlib.colors.Normalize()
+ elif (cbnorm == 'symlog'):
+ if cblinthresh is None:
+ cblinthresh = (data.max()-data.min())/10.
+ norm = matplotlib.colors.SymLogNorm(cblinthresh,vmin=data.min(), vmax=data.max())
extent = [float(e) for e in extent]
if isinstance(cmap, tuple):
if has_brewer:
@@ -126,7 +130,16 @@
self.image = self.axes.imshow(data.to_ndarray(), origin='lower',
extent=extent, norm=norm, vmin=self.zmin,
aspect=aspect, vmax=self.zmax, cmap=cmap)
- self.cb = self.figure.colorbar(self.image, self.cax)
+ if (cbnorm == 'symlog'):
+ formatter = matplotlib.ticker.LogFormatterMathtext()
+ self.cb = self.figure.colorbar(self.image, self.cax, format=formatter)
+ yticks = list(-10**np.arange(np.floor(np.log10(-data.min())),\
+ np.rint(np.log10(cblinthresh))-1, -1)) + [0] + \
+ list(10**np.arange(np.rint(np.log10(cblinthresh)),\
+ np.ceil(np.log10(data.max()))+1))
+ self.cb.set_ticks(yticks)
+ else:
+ self.cb = self.figure.colorbar(self.image, self.cax)
def _repr_png_(self):
canvas = FigureCanvasAgg(self.figure)
diff -r 5d42ecd2148572be9cec39c552443987aece9ffd -r 93d77640b4161deab1d3652a364bd902a60a3aa1 yt/visualization/plot_container.py
--- a/yt/visualization/plot_container.py
+++ b/yt/visualization/plot_container.py
@@ -80,6 +80,53 @@
return args[0]
return newfunc
+def get_log_minorticks(vmin, vmax):
+ """calculate positions of linear minorticks on a log colorbar
+
+ Parameters
+ ----------
+ vmin : float
+ the minimum value in the colorbar
+ vmax : float
+ the maximum value in the colorbar
+
+ """
+ expA = np.floor(np.log10(vmin))
+ expB = np.floor(np.log10(vmax))
+ cofA = np.ceil(vmin/10**expA)
+ cofB = np.floor(vmax/10**expB)
+ lmticks = []
+ while cofA*10**expA <= cofB*10**expB:
+ if expA < expB:
+ lmticks = np.hstack( (lmticks, np.linspace(cofA, 9, 10-cofA)*10**expA) )
+ cofA = 1
+ expA += 1
+ else:
+ lmticks = np.hstack( (lmticks, np.linspace(cofA, cofB, cofB-cofA+1)*10**expA) )
+ expA += 1
+ return np.array(lmticks)
+
+def get_symlog_minorticks(linthresh, vmin, vmax):
+ """calculate positions of linear minorticks on a symmetric log colorbar
+
+ Parameters
+ ----------
+ linthresh: float
+ the threshold for the linear region
+ vmin : float
+ the minimum value in the colorbar
+ vmax : float
+ the maximum value in the colorbar
+
+ """
+ if vmin >= 0 or vmax <= 0:
+ raise RuntimeError(
+ '''attempting to set minorticks for
+ a symlog plot with one-sided data:
+ got vmin = %s, vmax = %s''' % (vmin, vmax))
+ return np.hstack( (-get_log_minorticks(linthresh,-vmin)[::-1], 0,
+ get_log_minorticks(linthresh, vmax)) )
+
field_transforms = {}
@@ -102,6 +149,7 @@
log_transform = FieldTransform('log10', np.log10, LogLocator())
linear_transform = FieldTransform('linear', lambda x: x, LinearLocator())
+symlog_transform = FieldTransform('symlog', None, LogLocator())
class PlotDictionary(defaultdict):
def __getitem__(self, item):
@@ -143,11 +191,13 @@
self._font_color = None
self._xlabel = None
self._ylabel = None
+ self._minorticks = {}
+ self._cbar_minorticks = {}
self._colorbar_label = PlotDictionary(
self.data_source, lambda: None)
@invalidate_plot
- def set_log(self, field, log):
+ def set_log(self, field, log, linthresh=None):
"""set a field to log or linear.
Parameters
@@ -156,6 +206,8 @@
the field to set a transform
log : boolean
Log on/off.
+ linthresh : float (must be positive)
+ linthresh will be enabled for symlog scale only when log is true
"""
if field == 'all':
@@ -164,7 +216,13 @@
fields = [field]
for field in self.data_source._determine_fields(fields):
if log:
- self._field_transform[field] = log_transform
+ if linthresh is not None:
+ if not linthresh > 0.:
+ raise ValueError('\"linthresh\" must be positive')
+ self._field_transform[field] = symlog_transform
+ self._field_transform[field].func = linthresh
+ else:
+ self._field_transform[field] = log_transform
else:
self._field_transform[field] = linear_transform
return self
@@ -271,6 +329,58 @@
self.plots[field].zmax = myzmax
return self
+ @invalidate_plot
+ def set_minorticks(self, field, state):
+ """turn minor ticks on or off in the current plot
+
+ Displaying minor ticks reduces performance; turn them off
+ using set_minorticks('all', 'off') if drawing speed is a problem.
+
+ Parameters
+ ----------
+ field : string
+ the field to remove minorticks
+ state : string
+ the state indicating 'on' or 'off'
+
+ """
+ if field == 'all':
+ fields = self.plots.keys()
+ else:
+ fields = [field]
+ for field in self.data_source._determine_fields(fields):
+ if state == 'on':
+ self._minorticks[field] = True
+ else:
+ self._minorticks[field] = False
+ return self
+
+ @invalidate_plot
+ def set_cbar_minorticks(self, field, state):
+ """turn colorbar minor ticks on or off in the current plot
+
+ Displaying minor ticks reduces performance; turn them off
+ using set_cbar_minorticks('all', 'off') if drawing speed is a problem.
+
+ Parameters
+ ----------
+ field : string
+ the field to remove colorbar minorticks
+ state : string
+ the state indicating 'on' or 'off'
+
+ """
+ if field == 'all':
+ fields = self.plots.keys()
+ else:
+ fields = [field]
+ for field in self.data_source._determine_fields(fields):
+ if state == 'on':
+ self._cbar_minorticks[field] = True
+ else:
+ self._cbar_minorticks[field] = False
+ return self
+
def setup_callbacks(self):
# Left blank to be overriden in subclasses
pass
This diff is so big that we needed to truncate the remainder.
https://bitbucket.org/yt_analysis/yt/commits/32e52b00477a/
Changeset: 32e52b00477a
Branch: yt
User: jzuhone
Date: 2014-10-22 19:51:48+00:00
Summary: Some speedups
Affected #: 3 files
diff -r 93d77640b4161deab1d3652a364bd902a60a3aa1 -r 32e52b00477a8f3bc3e080901db354da1a13f67d yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -24,13 +24,20 @@
from yt.utilities.parallel_tools.parallel_analysis_interface import \
parallel_root_only
import re
+import ppv_utils
-def create_vlos(z_hat):
- def _v_los(field, data):
- vz = data["velocity_x"]*z_hat[0] + \
- data["velocity_y"]*z_hat[1] + \
- data["velocity_z"]*z_hat[2]
- return -vz
+def create_vlos(normal):
+ if isinstance(normal, basestring):
+ def _v_los(field, data):
+ return -data["velocity_%s" % normal]
+ else:
+ orient = Orientation(normal)
+ los_vec = orient.unit_vectors[2]
+ def _v_los(field, data):
+ vz = data["velocity_x"]*los_vec[0] + \
+ data["velocity_y"]*los_vec[1] + \
+ data["velocity_z"]*los_vec[2]
+ return -vz
return _v_los
fits_info = {"velocity":("m/s","VELOCITY","v"),
@@ -89,19 +96,6 @@
self.ny = dims[1]
self.nv = dims[2]
- if isinstance(normal, basestring):
- los_vec = np.zeros(3)
- los_vec[ds.coordinates.axis_id[normal]] = 1.0
- else:
- normal = np.array(normal)
- normal /= np.sqrt(np.dot(normal, normal))
- vecs = np.identity(3)
- t = np.cross(normal, vecs).sum(axis=1)
- ax = t.argmax()
- north = np.cross(normal, vecs[ax,:]).ravel()
- orient = Orientation(normal, north_vector=north)
- los_vec = orient.unit_vectors[2]
-
dd = ds.all_data()
fd = dd._determine_fields(field)[0]
@@ -118,35 +112,31 @@
self.vbins = np.linspace(self.v_bnd[0], self.v_bnd[1], num=self.nv+1)
self._vbins = self.vbins.copy()
self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
- self.vmid_cgs = self.vmid.in_cgs()
+ self.vmid_cgs = self.vmid.in_cgs().v
self.dv = self.vbins[1]-self.vbins[0]
- self.dv_cgs = self.dv.in_cgs()
+ self.dv_cgs = self.dv.in_cgs().v
- _vlos = create_vlos(los_vec)
- self.ds.field_info.add_field(("gas","v_los"), function=_vlos, units="cm/s")
+ self.current_v = 0.0
- if thermal_broad:
- self.v2_th = lambda T: 2.*kboltz*T/self.particle_mass
- self.phi_th = lambda v, T: self.dv_cgs*np.exp(-v*v/self.v2_th(T))/(np.sqrt(np.pi*self.v2_th(T)))
- else:
- self.v2_th = lambda T: 1.0
- self.phi_th = lambda v, T: np.maximum(1.-np.abs(v)/self.dv_cgs,0.0)
+ _vlos = create_vlos(normal)
+ self.ds.add_field(("gas","v_los"), function=_vlos, units="cm/s")
+
+ _intensity = self.create_intensity()
+ self.ds.add_field(("gas","intensity"), function=_intensity, units=self.field_units)
self.proj_units = str(ds.quan(1.0, self.field_units+"*cm").units)
self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.proj_units)
pbar = get_pbar("Generating cube.", self.nv)
for i in xrange(self.nv):
- _intensity = self._create_intensity(i)
- ds.add_field(("gas","intensity"), function=_intensity, units=self.field_units)
+ self.current_v = self.vmid_cgs[i]
if isinstance(normal, basestring):
prj = ds.proj("intensity", ds.coordinates.axis_id[normal])
buf = prj.to_frb(width, self.nx, center=self.center)["intensity"]
else:
buf = off_axis_projection(ds, self.center, normal, width,
- (self.nx, self.ny), "intensity")[::-1]
+ (self.nx, self.ny), "intensity", no_ghost=True)[::-1]
self.data[:,:,i] = buf[:,:]
- ds.field_info.pop(("gas","intensity"))
pbar.update(i)
pbar.finish()
@@ -158,6 +148,16 @@
else:
self.width = ds.quan(self.width, "code_length")
+ def create_intensity(self):
+ def _intensity(field, data):
+ v = self.current_v-data["v_los"].v
+ T = data["temperature"].v
+ w = ppv_utils.compute_weight(self.thermal_broad, self.dv_cgs,
+ self.particle_mass, v.flatten(), T.flatten())
+ w[np.isnan(w)] = 0.0
+ return data[self.field]*w.reshape(v.shape)
+ return _intensity
+
def transform_spectral_axis(self, rest_value, units):
"""
Change the units of the spectral axis to some equivalent unit, such
@@ -268,13 +268,6 @@
fib.writeto(filename, clobber=clobber)
- def _create_intensity(self, i):
- def _intensity(field, data):
- w = self.phi_th(self.vmid_cgs[i]-data["v_los"], data["temperature"])
- w[np.isnan(w)] = 0.0
- return data[self.field]*w
- return _intensity
-
def __repr__(self):
return "PPVCube [%d %d %d] (%s < %s < %s)" % (self.nx, self.ny, self.nv,
self.vbins[0],
diff -r 93d77640b4161deab1d3652a364bd902a60a3aa1 -r 32e52b00477a8f3bc3e080901db354da1a13f67d yt/analysis_modules/ppv_cube/ppv_utils.pyx
--- /dev/null
+++ b/yt/analysis_modules/ppv_cube/ppv_utils.pyx
@@ -0,0 +1,40 @@
+import numpy as np
+cimport numpy as np
+cimport cython
+from yt.utilities.physical_constants import kboltz
+
+cdef extern from "math.h":
+ double exp(double x) nogil
+ double fabs(double x) nogil
+ double sqrt(double x) nogil
+
+cdef double kb = kboltz.v
+cdef double pi = np.pi
+
+ at cython.cdivision(True)
+ at cython.boundscheck(False)
+ at cython.wraparound(False)
+def compute_weight(np.uint8_t thermal_broad,
+ double dv,
+ double m_part,
+ np.ndarray[np.float64_t, ndim=1] v,
+ np.ndarray[np.float64_t, ndim=1] T):
+
+ cdef int i, n
+ cdef double v2_th, x
+ cdef np.ndarray[np.float64_t, ndim=1] w
+
+ n = v.shape[0]
+ w = np.zeros(n)
+
+ for i in range(n):
+ if thermal_broad:
+ if T[i] == 0.0:
+ v2_th = 2.*kb*T[i]/m_part
+ w[i] = dv*exp(-v[i]*v[i]/v2_th)/(sqrt(v2_th)*pi)
+ else:
+ x = 1.-fabs(v[i])/dv
+ if x > 0.0:
+ w[i] = x
+
+ return w
diff -r 93d77640b4161deab1d3652a364bd902a60a3aa1 -r 32e52b00477a8f3bc3e080901db354da1a13f67d yt/analysis_modules/ppv_cube/setup.py
--- a/yt/analysis_modules/ppv_cube/setup.py
+++ b/yt/analysis_modules/ppv_cube/setup.py
@@ -8,6 +8,9 @@
def configuration(parent_package='', top_path=None):
from numpy.distutils.misc_util import Configuration
config = Configuration('ppv_cube', parent_package, top_path)
+ config.add_extension("ppv_utils",
+ ["yt/analysis_modules/ppv_cube/ppv_utils.pyx"],
+ libraries=["m"])
#config.add_subpackage("tests")
config.make_config_py() # installs __config__.py
#config.make_svn_version_py()
https://bitbucket.org/yt_analysis/yt/commits/b4a9fc99a183/
Changeset: b4a9fc99a183
Branch: yt
User: jzuhone
Date: 2014-10-24 15:29:10+00:00
Summary: Stoopid
Affected #: 1 file
diff -r 32e52b00477a8f3bc3e080901db354da1a13f67d -r b4a9fc99a18392e3d7c89aa476af282e23788525 yt/analysis_modules/ppv_cube/ppv_utils.pyx
--- a/yt/analysis_modules/ppv_cube/ppv_utils.pyx
+++ b/yt/analysis_modules/ppv_cube/ppv_utils.pyx
@@ -17,8 +17,8 @@
def compute_weight(np.uint8_t thermal_broad,
double dv,
double m_part,
- np.ndarray[np.float64_t, ndim=1] v,
- np.ndarray[np.float64_t, ndim=1] T):
+ np.ndarray[np.float64_t, ndim=1] v,
+ np.ndarray[np.float64_t, ndim=1] T):
cdef int i, n
cdef double v2_th, x
@@ -29,7 +29,7 @@
for i in range(n):
if thermal_broad:
- if T[i] == 0.0:
+ if T[i] > 0.0:
v2_th = 2.*kb*T[i]/m_part
w[i] = dv*exp(-v[i]*v[i]/v2_th)/(sqrt(v2_th)*pi)
else:
https://bitbucket.org/yt_analysis/yt/commits/f3183595882f/
Changeset: f3183595882f
Branch: yt
User: jzuhone
Date: 2014-10-24 17:21:00+00:00
Summary: Bug fixes
Affected #: 2 files
diff -r b4a9fc99a18392e3d7c89aa476af282e23788525 -r f3183595882fe7057b806054b87f90ab7917e687 yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -152,8 +152,8 @@
def _intensity(field, data):
v = self.current_v-data["v_los"].v
T = data["temperature"].v
- w = ppv_utils.compute_weight(self.thermal_broad, self.dv_cgs,
- self.particle_mass, v.flatten(), T.flatten())
+ w = ppv_utils.compute_weight(self.thermal_broad, self.dv_cgs,
+ self.particle_mass, v.flatten(), T.flatten())
w[np.isnan(w)] = 0.0
return data[self.field]*w.reshape(v.shape)
return _intensity
diff -r b4a9fc99a18392e3d7c89aa476af282e23788525 -r f3183595882fe7057b806054b87f90ab7917e687 yt/analysis_modules/ppv_cube/ppv_utils.pyx
--- a/yt/analysis_modules/ppv_cube/ppv_utils.pyx
+++ b/yt/analysis_modules/ppv_cube/ppv_utils.pyx
@@ -31,7 +31,7 @@
if thermal_broad:
if T[i] > 0.0:
v2_th = 2.*kb*T[i]/m_part
- w[i] = dv*exp(-v[i]*v[i]/v2_th)/(sqrt(v2_th)*pi)
+ w[i] = dv*exp(-v[i]*v[i]/v2_th)/sqrt(v2_th*pi)
else:
x = 1.-fabs(v[i])/dv
if x > 0.0:
https://bitbucket.org/yt_analysis/yt/commits/c845dc77c701/
Changeset: c845dc77c701
Branch: yt
User: jzuhone
Date: 2014-10-24 17:21:11+00:00
Summary: Adding a PPVCube test.
Affected #: 2 files
diff -r f3183595882fe7057b806054b87f90ab7917e687 -r c845dc77c701403783cad26030908d094608ff74 yt/analysis_modules/ppv_cube/setup.py
--- a/yt/analysis_modules/ppv_cube/setup.py
+++ b/yt/analysis_modules/ppv_cube/setup.py
@@ -11,7 +11,7 @@
config.add_extension("ppv_utils",
["yt/analysis_modules/ppv_cube/ppv_utils.pyx"],
libraries=["m"])
- #config.add_subpackage("tests")
+ config.add_subpackage("tests")
config.make_config_py() # installs __config__.py
#config.make_svn_version_py()
return config
https://bitbucket.org/yt_analysis/yt/commits/609efd317364/
Changeset: 609efd317364
Branch: yt
User: jzuhone
Date: 2014-10-24 17:55:03+00:00
Summary: How about we actually add the test this time?
Affected #: 1 file
diff -r c845dc77c701403783cad26030908d094608ff74 -r 609efd317364ae59d6f1418dda21ddb09d699caa yt/analysis_modules/ppv_cube/tests/test_ppv.py
--- /dev/null
+++ b/yt/analysis_modules/ppv_cube/tests/test_ppv.py
@@ -0,0 +1,60 @@
+"""
+Unit test the sunyaev_zeldovich analysis module.
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2013, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+from yt.frontends.stream.api import load_uniform_grid
+from yt.analysis_modules.ppv_cube.api import PPVCube
+import yt.units as u
+from yt.utilities.physical_constants import kboltz, mh, clight
+import numpy as np
+from yt.testing import *
+
+def setup():
+ """Test specific setup."""
+ from yt.config import ytcfg
+ ytcfg["yt", "__withintesting"] = "True"
+
+def test_ppv():
+
+ dims = (100,100,100)
+ v_shift = 1.0e7*u.cm/u.s
+ sigma_v = 2.0e7*u.cm/u.s
+ T_0 = 1.0e8*u.Kelvin
+ data = {"density":(np.ones(dims),"g/cm**3"),
+ "temperature":(T_0.v*np.ones(dims), "K"),
+ "velocity_x":(np.zeros(dims),"cm/s"),
+ "velocity_y":(np.zeros(dims),"cm/s"),
+ "velocity_z":(np.random.normal(loc=v_shift.v,scale=sigma_v.v,size=dims), "cm/s")}
+
+ ds = load_uniform_grid(data, dims)
+
+ cube = PPVCube(ds, "z", "density", dims=dims,
+ velocity_bounds=(-300., 300., "km/s"),
+ thermal_broad=True)
+
+ dv = cube.dv
+ v_th = np.sqrt(2.*kboltz*T_0/(56.*mh) + 2.*sigma_v**2).in_units("km/s")
+ a = cube.data.mean(axis=(0,1)).v
+ b = dv*np.exp(-((cube.vmid+v_shift)/v_th)**2)/(np.sqrt(np.pi)*v_th)
+
+ yield assert_allclose, a, b, 2.0e-3
+
+ E_0 = 6.8*u.keV
+
+ cube.transform_spectral_axis(E_0.v, str(E_0.units))
+
+ dE = -cube.dv
+ delta_E = E_0*v_th.in_cgs()/clight
+ E_shift = E_0*(1.+v_shift/clight)
+
+ c = dE*np.exp(-((cube.vmid-E_shift)/delta_E)**2)/(np.sqrt(np.pi)*delta_E)
+
+ yield assert_allclose, a, c, 2.0e-3
https://bitbucket.org/yt_analysis/yt/commits/ea7e7a2406fd/
Changeset: ea7e7a2406fd
Branch: yt
User: jzuhone
Date: 2014-10-24 18:15:44+00:00
Summary: Parallelizing PPVCube.
Affected #: 1 file
diff -r 609efd317364ae59d6f1418dda21ddb09d699caa -r ea7e7a2406fd0676485649f63dd97745d5d532ff yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -16,15 +16,14 @@
from yt.utilities.fits_image import FITSImageBuffer
from yt.visualization.volume_rendering.camera import off_axis_projection
from yt.funcs import get_pbar
-from yt.utilities.physical_constants import clight, mh, kboltz
+from yt.utilities.physical_constants import clight, mh
import yt.units.dimensions as ytdims
-import yt.units as u
-from yt.units.yt_array import YTQuantity
from yt.funcs import iterable
from yt.utilities.parallel_tools.parallel_analysis_interface import \
- parallel_root_only
+ parallel_root_only, parallel_objects
import re
import ppv_utils
+from yt.funcs import is_root
def create_vlos(normal):
if isinstance(normal, basestring):
@@ -127,8 +126,9 @@
self.proj_units = str(ds.quan(1.0, self.field_units+"*cm").units)
self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.proj_units)
+ storage = {}
pbar = get_pbar("Generating cube.", self.nv)
- for i in xrange(self.nv):
+ for sto, i in parallel_objects(xrange(self.nv), storage=storage):
self.current_v = self.vmid_cgs[i]
if isinstance(normal, basestring):
prj = ds.proj("intensity", ds.coordinates.axis_id[normal])
@@ -136,10 +136,16 @@
else:
buf = off_axis_projection(ds, self.center, normal, width,
(self.nx, self.ny), "intensity", no_ghost=True)[::-1]
- self.data[:,:,i] = buf[:,:]
+ sto.result_id = i
+ sto.result = buf
pbar.update(i)
pbar.finish()
+ self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.proj_units)
+ if is_root():
+ for i, buf in sorted(storage.items()):
+ self.data[:,:,i] = buf[:,:]
+
self.axis_type = "velocity"
# Now fix the width
https://bitbucket.org/yt_analysis/yt/commits/31917ddf10bd/
Changeset: 31917ddf10bd
Branch: yt
User: jzuhone
Date: 2014-10-25 16:25:25+00:00
Summary: This is all we can reasonably expect out of this test
Affected #: 1 file
diff -r ea7e7a2406fd0676485649f63dd97745d5d532ff -r 31917ddf10bd1c43eacd26b6963e32544254e976 yt/analysis_modules/ppv_cube/tests/test_ppv.py
--- a/yt/analysis_modules/ppv_cube/tests/test_ppv.py
+++ b/yt/analysis_modules/ppv_cube/tests/test_ppv.py
@@ -24,7 +24,7 @@
def test_ppv():
- dims = (100,100,100)
+ dims = (8,8,1024)
v_shift = 1.0e7*u.cm/u.s
sigma_v = 2.0e7*u.cm/u.s
T_0 = 1.0e8*u.Kelvin
@@ -45,7 +45,7 @@
a = cube.data.mean(axis=(0,1)).v
b = dv*np.exp(-((cube.vmid+v_shift)/v_th)**2)/(np.sqrt(np.pi)*v_th)
- yield assert_allclose, a, b, 2.0e-3
+ yield assert_allclose, a, b, 1.0e-2
E_0 = 6.8*u.keV
@@ -57,4 +57,4 @@
c = dE*np.exp(-((cube.vmid-E_shift)/delta_E)**2)/(np.sqrt(np.pi)*delta_E)
- yield assert_allclose, a, c, 2.0e-3
+ yield assert_allclose, a, c, 1.0e-2
https://bitbucket.org/yt_analysis/yt/commits/676347e40516/
Changeset: 676347e40516
Branch: yt
User: jzuhone
Date: 2014-10-27 17:34:49+00:00
Summary: Allowing for custom widths in constructing FITS images
Affected #: 1 file
diff -r 31917ddf10bd1c43eacd26b6963e32544254e976 -r 676347e40516e01be2a50f3099bf87ddf7e6744b yt/utilities/fits_image.py
--- a/yt/utilities/fits_image.py
+++ b/yt/utilities/fits_image.py
@@ -232,31 +232,46 @@
axis_wcs = [[1,2],[0,2],[0,1]]
-def construct_image(data_source, center=None):
+def sanitize_fits_unit(unit, dx):
+ if unit == "Mpc":
+ mylog.info("Changing FITS file unit to kpc.")
+ unit = "kpc"
+ dx *= 1000.
+ elif unit == "au":
+ unit = "AU"
+ return unit, dx
+
+def construct_image(data_source, center=None, width=None):
ds = data_source.ds
axis = data_source.axis
+ if center is None or width is None:
+ center = ds.domain_center[axis_wcs[axis]]
+ if width is None:
+ width = ds.domain_width[axis_wcs[axis]]
+ mylog.info("Making an image of the entire domain, "+
+ "so setting the center to the domain center.")
+ else:
+ width = ds.coordinates.sanitize_width(axis, width, None)
+ dx = ds.index.get_smallest_dx()
+ nx, ny = [int((w/dx).in_units("dimensionless")) for w in width]
+ crpix = [0.5*(nx+1), 0.5*(ny+1)]
if hasattr(ds, "wcs"):
- # This is a FITS dataset
- nx, ny = ds.domain_dimensions[axis_wcs[axis]]
- crpix = [ds.wcs.wcs.crpix[idx] for idx in axis_wcs[axis]]
- cdelt = [ds.wcs.wcs.cdelt[idx] for idx in axis_wcs[axis]]
- crval = [ds.wcs.wcs.crval[idx] for idx in axis_wcs[axis]]
+ # This is a FITS dataset, so we use it to construct the WCS
cunit = [str(ds.wcs.wcs.cunit[idx]) for idx in axis_wcs[axis]]
ctype = [ds.wcs.wcs.ctype[idx] for idx in axis_wcs[axis]]
+ crval = [ds.wcs.wcs.crval[idx] for idx in axis_wcs[axis]]
+ cdelt = [ds.wcs.wcs.cdelt[idx] for idx in axis_wcs[axis]]
else:
- # This is some other kind of dataset
- unit = ds.get_smallest_appropriate_unit(ds.domain_width.max())
- if center is None:
- crval = [0.0,0.0]
- else:
- crval = [(ds.domain_center-center)[idx].in_units(unit) for idx in axis_wcs[axis]]
- dx = ds.index.get_smallest_dx()
- nx, ny = (ds.domain_width[axis_wcs[axis]]/dx).ndarray_view().astype("int")
- crpix = [0.5*(nx+1), 0.5*(ny+1)]
- cdelt = [dx.in_units(unit)]*2
+ # This is some other kind of dataset
+ unit = str(width[0].units)
+ if unit == "unitary":
+ unit = ds.get_smallest_appropriate_unit(ds.domain_width.max())
+ unit = sanitize_fits_unit(unit)
cunit = [unit]*2
ctype = ["LINEAR"]*2
- frb = data_source.to_frb((1.0,"unitary"), (nx,ny))
+ crval = [center[idx].in_units(unit) for idx in axis_wcs[axis]]
+ cdelt = [dx.in_units(unit)]*2
+ frb = data_source.to_frb(width[0], (nx,ny), center=center, height=width[1])
w = pywcs.WCS(naxis=2)
w.wcs.crpix = crpix
w.wcs.cdelt = cdelt
@@ -285,13 +300,14 @@
as a tuple containing a coordinate and string unit name or by passing
in a YTArray. If a list or unitless array is supplied, code units are
assumed.
+ width :
"""
- def __init__(self, ds, axis, fields, center="c", **kwargs):
+ def __init__(self, ds, axis, fields, center="c", width=None, **kwargs):
fields = ensure_list(fields)
axis = fix_axis(axis, ds)
center = ds.coordinates.sanitize_center(center, axis)
slc = ds.slice(axis, center[axis], **kwargs)
- w, frb = construct_image(slc, center=center)
+ w, frb = construct_image(slc, center=dcenter, width=width)
super(FITSSlice, self).__init__(frb, fields=fields, wcs=w)
for i, field in enumerate(fields):
self[i].header["bunit"] = str(frb[field].units)
@@ -318,13 +334,14 @@
as a tuple containing a coordinate and string unit name or by passing
in a YTArray. If a list or unitless array is supplied, code units are
assumed.
+ width :
"""
- def __init__(self, ds, axis, fields, center="c", weight_field=None, **kwargs):
+ def __init__(self, ds, axis, fields, center="c", width=None, weight_field=None, **kwargs):
fields = ensure_list(fields)
axis = fix_axis(axis, ds)
- center = ds.coordinates.sanitize_center(center, axis)
+ center, dcenter = ds.coordinates.sanitize_center(center, axis)
prj = ds.proj(fields[0], axis, weight_field=weight_field, **kwargs)
- w, frb = construct_image(prj, center=center)
+ w, frb = construct_image(prj, center=dcenter, width=width)
super(FITSProjection, self).__init__(frb, fields=fields, wcs=w)
for i, field in enumerate(fields):
self[i].header["bunit"] = str(frb[field].units)
https://bitbucket.org/yt_analysis/yt/commits/08db763a0286/
Changeset: 08db763a0286
Branch: yt
User: jzuhone
Date: 2014-10-27 17:36:04+00:00
Summary: Using sanitize_fits_width here
Affected #: 2 files
diff -r 31917ddf10bd1c43eacd26b6963e32544254e976 -r 08db763a0286ccb433961d38cc7644bb8b46f7f4 yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -13,7 +13,7 @@
import numpy as np
from yt.utilities.on_demand_imports import _astropy
from yt.utilities.orientation import Orientation
-from yt.utilities.fits_image import FITSImageBuffer
+from yt.utilities.fits_image import FITSImageBuffer, sanitize_fits_unit
from yt.visualization.volume_rendering.camera import off_axis_projection
from yt.funcs import get_pbar
from yt.utilities.physical_constants import clight, mh
@@ -248,13 +248,8 @@
units = str(self.ds.get_smallest_appropriate_unit(self.width))
else:
units = length_unit
- dx = self.width.in_units(units).v/self.nx
- # Hacks because FITS is stupid and doesn't understand case
- if units == "Mpc":
- units = "kpc"
- dx *= 1000.
- elif units == "au":
- units = "AU"
+ units = sanitize_fits_unit(units)
+ dx = self.width.in_units(units).v/self.nx
dy = dx
dv = self.dv.in_units(vunit).v
diff -r 31917ddf10bd1c43eacd26b6963e32544254e976 -r 08db763a0286ccb433961d38cc7644bb8b46f7f4 yt/analysis_modules/sunyaev_zeldovich/projection.py
--- a/yt/analysis_modules/sunyaev_zeldovich/projection.py
+++ b/yt/analysis_modules/sunyaev_zeldovich/projection.py
@@ -309,7 +309,7 @@
>>> sky_center = (30., 45.) # In degrees
>>> szprj.write_fits("SZbullet.fits", sky_center=sky_center, sky_scale=sky_scale)
"""
- from yt.utilities.fits_image import FITSImageBuffer
+ from yt.utilities.fits_image import FITSImageBuffer, sanitize_fits_unit
if sky_scale is None:
center = (0.0,0.0)
@@ -324,9 +324,8 @@
types = ["RA---TAN","DEC--TAN"]
units = self.ds.get_smallest_appropriate_unit(self.width)
+ units = sanitize_fits_unit(units)
# Hack because FITS is stupid and doesn't understand case
- if units == "Mpc":
- units = "kpc"
dx = self.dx.in_units(units)
if sky_scale:
dx = (dx*sky_scale).in_units("deg")
https://bitbucket.org/yt_analysis/yt/commits/3f9b94d346e8/
Changeset: 3f9b94d346e8
Branch: yt
User: jzuhone
Date: 2014-10-27 17:36:18+00:00
Summary: Merge
Affected #: 1 file
diff -r 08db763a0286ccb433961d38cc7644bb8b46f7f4 -r 3f9b94d346e8a71be62debc9c3740e9612d6204b yt/utilities/fits_image.py
--- a/yt/utilities/fits_image.py
+++ b/yt/utilities/fits_image.py
@@ -232,31 +232,46 @@
axis_wcs = [[1,2],[0,2],[0,1]]
-def construct_image(data_source, center=None):
+def sanitize_fits_unit(unit, dx):
+ if unit == "Mpc":
+ mylog.info("Changing FITS file unit to kpc.")
+ unit = "kpc"
+ dx *= 1000.
+ elif unit == "au":
+ unit = "AU"
+ return unit, dx
+
+def construct_image(data_source, center=None, width=None):
ds = data_source.ds
axis = data_source.axis
+ if center is None or width is None:
+ center = ds.domain_center[axis_wcs[axis]]
+ if width is None:
+ width = ds.domain_width[axis_wcs[axis]]
+ mylog.info("Making an image of the entire domain, "+
+ "so setting the center to the domain center.")
+ else:
+ width = ds.coordinates.sanitize_width(axis, width, None)
+ dx = ds.index.get_smallest_dx()
+ nx, ny = [int((w/dx).in_units("dimensionless")) for w in width]
+ crpix = [0.5*(nx+1), 0.5*(ny+1)]
if hasattr(ds, "wcs"):
- # This is a FITS dataset
- nx, ny = ds.domain_dimensions[axis_wcs[axis]]
- crpix = [ds.wcs.wcs.crpix[idx] for idx in axis_wcs[axis]]
- cdelt = [ds.wcs.wcs.cdelt[idx] for idx in axis_wcs[axis]]
- crval = [ds.wcs.wcs.crval[idx] for idx in axis_wcs[axis]]
+ # This is a FITS dataset, so we use it to construct the WCS
cunit = [str(ds.wcs.wcs.cunit[idx]) for idx in axis_wcs[axis]]
ctype = [ds.wcs.wcs.ctype[idx] for idx in axis_wcs[axis]]
+ crval = [ds.wcs.wcs.crval[idx] for idx in axis_wcs[axis]]
+ cdelt = [ds.wcs.wcs.cdelt[idx] for idx in axis_wcs[axis]]
else:
- # This is some other kind of dataset
- unit = ds.get_smallest_appropriate_unit(ds.domain_width.max())
- if center is None:
- crval = [0.0,0.0]
- else:
- crval = [(ds.domain_center-center)[idx].in_units(unit) for idx in axis_wcs[axis]]
- dx = ds.index.get_smallest_dx()
- nx, ny = (ds.domain_width[axis_wcs[axis]]/dx).ndarray_view().astype("int")
- crpix = [0.5*(nx+1), 0.5*(ny+1)]
- cdelt = [dx.in_units(unit)]*2
+ # This is some other kind of dataset
+ unit = str(width[0].units)
+ if unit == "unitary":
+ unit = ds.get_smallest_appropriate_unit(ds.domain_width.max())
+ unit = sanitize_fits_unit(unit)
cunit = [unit]*2
ctype = ["LINEAR"]*2
- frb = data_source.to_frb((1.0,"unitary"), (nx,ny))
+ crval = [center[idx].in_units(unit) for idx in axis_wcs[axis]]
+ cdelt = [dx.in_units(unit)]*2
+ frb = data_source.to_frb(width[0], (nx,ny), center=center, height=width[1])
w = pywcs.WCS(naxis=2)
w.wcs.crpix = crpix
w.wcs.cdelt = cdelt
@@ -285,13 +300,14 @@
as a tuple containing a coordinate and string unit name or by passing
in a YTArray. If a list or unitless array is supplied, code units are
assumed.
+ width :
"""
- def __init__(self, ds, axis, fields, center="c", **kwargs):
+ def __init__(self, ds, axis, fields, center="c", width=None, **kwargs):
fields = ensure_list(fields)
axis = fix_axis(axis, ds)
center = ds.coordinates.sanitize_center(center, axis)
slc = ds.slice(axis, center[axis], **kwargs)
- w, frb = construct_image(slc, center=center)
+ w, frb = construct_image(slc, center=dcenter, width=width)
super(FITSSlice, self).__init__(frb, fields=fields, wcs=w)
for i, field in enumerate(fields):
self[i].header["bunit"] = str(frb[field].units)
@@ -318,13 +334,14 @@
as a tuple containing a coordinate and string unit name or by passing
in a YTArray. If a list or unitless array is supplied, code units are
assumed.
+ width :
"""
- def __init__(self, ds, axis, fields, center="c", weight_field=None, **kwargs):
+ def __init__(self, ds, axis, fields, center="c", width=None, weight_field=None, **kwargs):
fields = ensure_list(fields)
axis = fix_axis(axis, ds)
- center = ds.coordinates.sanitize_center(center, axis)
+ center, dcenter = ds.coordinates.sanitize_center(center, axis)
prj = ds.proj(fields[0], axis, weight_field=weight_field, **kwargs)
- w, frb = construct_image(prj, center=center)
+ w, frb = construct_image(prj, center=dcenter, width=width)
super(FITSProjection, self).__init__(frb, fields=fields, wcs=w)
for i, field in enumerate(fields):
self[i].header["bunit"] = str(frb[field].units)
https://bitbucket.org/yt_analysis/yt/commits/49ea8a1c2c0f/
Changeset: 49ea8a1c2c0f
Branch: yt
User: jzuhone
Date: 2014-10-27 17:38:04+00:00
Summary: Bug fix
Affected #: 1 file
diff -r 3f9b94d346e8a71be62debc9c3740e9612d6204b -r 49ea8a1c2c0f9470c415ad879251e3851ea2a402 yt/utilities/fits_image.py
--- a/yt/utilities/fits_image.py
+++ b/yt/utilities/fits_image.py
@@ -232,14 +232,13 @@
axis_wcs = [[1,2],[0,2],[0,1]]
-def sanitize_fits_unit(unit, dx):
+def sanitize_fits_unit(unit):
if unit == "Mpc":
mylog.info("Changing FITS file unit to kpc.")
unit = "kpc"
- dx *= 1000.
elif unit == "au":
unit = "AU"
- return unit, dx
+ return unit
def construct_image(data_source, center=None, width=None):
ds = data_source.ds
https://bitbucket.org/yt_analysis/yt/commits/82510469b48b/
Changeset: 82510469b48b
Branch: yt
User: jzuhone
Date: 2014-10-27 18:55:38+00:00
Summary: Adding a way to represent unit prefixes as LaTeX. Currently only works for "u" / "mu" (micro).
Affected #: 3 files
diff -r 49ea8a1c2c0f9470c415ad879251e3851ea2a402 -r 82510469b48b6e805d285098cb1b667017d448ed yt/units/unit_lookup_table.py
--- a/yt/units/unit_lookup_table.py
+++ b/yt/units/unit_lookup_table.py
@@ -138,6 +138,10 @@
if key not in latex_symbol_lut:
latex_symbol_lut[key] = "\\rm{" + key + "}"
+latex_prefixes = {
+ "u" : "\\mu",
+}
+
# This dictionary formatting from magnitude package, credit to Juan Reyero.
unit_prefixes = {
'Y': 1e24, # yotta
@@ -177,6 +181,7 @@
"Hz",
"W",
"gauss",
+ "G",
"Jy",
)
diff -r 49ea8a1c2c0f9470c415ad879251e3851ea2a402 -r 82510469b48b6e805d285098cb1b667017d448ed yt/units/unit_object.py
--- a/yt/units/unit_object.py
+++ b/yt/units/unit_object.py
@@ -26,7 +26,7 @@
from yt.units.unit_lookup_table import \
latex_symbol_lut, unit_prefixes, \
prefixable_units, cgs_base_units, \
- mks_base_units
+ mks_base_units, latex_prefixes
from yt.units.unit_registry import UnitRegistry
import copy
@@ -512,9 +512,14 @@
prefix_value = unit_prefixes[possible_prefix]
if symbol_str not in latex_symbol_lut:
+ if possible_prefix in latex_prefixes:
+ sstr = symbol_str.replace(possible_prefix,
+ '{'+latex_prefixes[possible_prefix]+'}')
+ else:
+ sstr = symbol_str
latex_symbol_lut[symbol_str] = \
latex_symbol_lut[symbol_wo_prefix].replace(
- '{'+symbol_wo_prefix+'}', '{'+symbol_str+'}')
+ '{'+symbol_wo_prefix+'}', '{'+sstr+'}')
# don't forget to account for the prefix value!
return (unit_data[0] * prefix_value, unit_data[1])
diff -r 49ea8a1c2c0f9470c415ad879251e3851ea2a402 -r 82510469b48b6e805d285098cb1b667017d448ed yt/utilities/fits_image.py
--- a/yt/utilities/fits_image.py
+++ b/yt/utilities/fits_image.py
@@ -15,7 +15,7 @@
from yt.visualization.fixed_resolution import FixedResolutionBuffer
from yt.data_objects.construction_data_containers import YTCoveringGridBase
from yt.utilities.on_demand_imports import _astropy
-from yt.units.yt_array import YTQuantity
+from yt.units.yt_array import YTQuantity, YTArray
import re
pyfits = _astropy.pyfits
@@ -101,8 +101,14 @@
first = True
+ self.field_units = {}
+
for key in fields:
if key not in exclude_fields:
+ if hasattr(img_data[key], "units"):
+ self.field_units[key] = str(img_data[key].units)
+ else:
+ self.field_units[key] = "dimensionless"
mylog.info("Making a FITS image of field %s" % (key))
if first:
hdu = pyfits.PrimaryHDU(np.array(img_data[key]))
@@ -230,6 +236,18 @@
import aplpy
return aplpy.FITSFigure(self, **kwargs)
+ def get_data(self, field):
+ return YTArray(self[field].data, self.field_units[field])
+
+ def set_unit(self, field, units):
+ """
+ Set the units of *field* to *units*.
+ """
+ new_data = YTArray(self[field].data, self.field_units[field]).in_units(units)
+ self[field].data = new_data.v
+ self[field].header["bunit"] = units
+ self.field_units[field] = units
+
axis_wcs = [[1,2],[0,2],[0,1]]
def sanitize_fits_unit(unit):
@@ -270,6 +288,7 @@
ctype = ["LINEAR"]*2
crval = [center[idx].in_units(unit) for idx in axis_wcs[axis]]
cdelt = [dx.in_units(unit)]*2
+ crpix = [0.5*(nx+1), 0.5*(ny+1)]
frb = data_source.to_frb(width[0], (nx,ny), center=center, height=width[1])
w = pywcs.WCS(naxis=2)
w.wcs.crpix = crpix
@@ -335,7 +354,8 @@
assumed.
width :
"""
- def __init__(self, ds, axis, fields, center="c", width=None, weight_field=None, **kwargs):
+ def __init__(self, ds, axis, fields, center="c", width=None,
+ weight_field=None, **kwargs):
fields = ensure_list(fields)
axis = fix_axis(axis, ds)
center, dcenter = ds.coordinates.sanitize_center(center, axis)
https://bitbucket.org/yt_analysis/yt/commits/ffa9561da09a/
Changeset: ffa9561da09a
Branch: yt
User: jzuhone
Date: 2014-10-27 19:11:40+00:00
Summary: Attempting to get this right for FITS datasets
Affected #: 1 file
diff -r 82510469b48b6e805d285098cb1b667017d448ed -r ffa9561da09ae368c9f70a9831d35b4300d29eae yt/utilities/fits_image.py
--- a/yt/utilities/fits_image.py
+++ b/yt/utilities/fits_image.py
@@ -276,8 +276,10 @@
# This is a FITS dataset, so we use it to construct the WCS
cunit = [str(ds.wcs.wcs.cunit[idx]) for idx in axis_wcs[axis]]
ctype = [ds.wcs.wcs.ctype[idx] for idx in axis_wcs[axis]]
- crval = [ds.wcs.wcs.crval[idx] for idx in axis_wcs[axis]]
cdelt = [ds.wcs.wcs.cdelt[idx] for idx in axis_wcs[axis]]
+ ctr_pix = center.in_units("code_length")[:self.dimensionality].v
+ crval = ds.wcs.wcs_pix2world(ctr_pix.reshape(1,self.dimensionality))[0]
+ crval = [crval[idx] for idx in axis_wcs[axis]]
else:
# This is some other kind of dataset
unit = str(width[0].units)
@@ -286,9 +288,9 @@
unit = sanitize_fits_unit(unit)
cunit = [unit]*2
ctype = ["LINEAR"]*2
+ cdelt = [dx.in_units(unit)]*2
crval = [center[idx].in_units(unit) for idx in axis_wcs[axis]]
- cdelt = [dx.in_units(unit)]*2
- crpix = [0.5*(nx+1), 0.5*(ny+1)]
+ crpix = [0.5*(nx+1), 0.5*(ny+1)]
frb = data_source.to_frb(width[0], (nx,ny), center=center, height=width[1])
w = pywcs.WCS(naxis=2)
w.wcs.crpix = crpix
@@ -323,7 +325,7 @@
def __init__(self, ds, axis, fields, center="c", width=None, **kwargs):
fields = ensure_list(fields)
axis = fix_axis(axis, ds)
- center = ds.coordinates.sanitize_center(center, axis)
+ center, dcenter = ds.coordinates.sanitize_center(center, axis)
slc = ds.slice(axis, center[axis], **kwargs)
w, frb = construct_image(slc, center=dcenter, width=width)
super(FITSSlice, self).__init__(frb, fields=fields, wcs=w)
https://bitbucket.org/yt_analysis/yt/commits/c22ef53e667c/
Changeset: c22ef53e667c
Branch: yt
User: jzuhone
Date: 2014-10-27 19:13:14+00:00
Summary: Removing extra line
Affected #: 1 file
diff -r ffa9561da09ae368c9f70a9831d35b4300d29eae -r c22ef53e667c9943357bee7ac5fac164eb896a95 yt/utilities/fits_image.py
--- a/yt/utilities/fits_image.py
+++ b/yt/utilities/fits_image.py
@@ -290,7 +290,6 @@
ctype = ["LINEAR"]*2
cdelt = [dx.in_units(unit)]*2
crval = [center[idx].in_units(unit) for idx in axis_wcs[axis]]
- crpix = [0.5*(nx+1), 0.5*(ny+1)]
frb = data_source.to_frb(width[0], (nx,ny), center=center, height=width[1])
w = pywcs.WCS(naxis=2)
w.wcs.crpix = crpix
https://bitbucket.org/yt_analysis/yt/commits/752db2db1ae0/
Changeset: 752db2db1ae0
Branch: yt
User: jzuhone
Date: 2014-10-27 19:45:04+00:00
Summary: make keys lowercase
Affected #: 1 file
diff -r c22ef53e667c9943357bee7ac5fac164eb896a95 -r 752db2db1ae0cba8e823303a5cc34d1b3a9c6ad0 yt/utilities/fits_image.py
--- a/yt/utilities/fits_image.py
+++ b/yt/utilities/fits_image.py
@@ -181,7 +181,7 @@
for img in self: img.header[key] = value
def keys(self):
- return [f.name for f in self]
+ return [f.name.lower() for f in self]
def has_key(self, key):
return key in self.keys()
https://bitbucket.org/yt_analysis/yt/commits/9a4b5263deaa/
Changeset: 9a4b5263deaa
Branch: yt
User: jzuhone
Date: 2014-10-28 19:35:22+00:00
Summary: Adding parentheses to the delimiters that shouldn't be checked
Affected #: 2 files
diff -r 752db2db1ae0cba8e823303a5cc34d1b3a9c6ad0 -r 9a4b5263deaa33b9bd2ea07834c1d6d6daf45740 yt/frontends/fits/data_structures.py
--- a/yt/frontends/fits/data_structures.py
+++ b/yt/frontends/fits/data_structures.py
@@ -46,7 +46,7 @@
lon_prefixes = ["X","RA","GLON","LINEAR"]
lat_prefixes = ["Y","DEC","GLAT","LINEAR"]
-delimiters = ["*", "/", "-", "^"]
+delimiters = ["*", "/", "-", "^", "(", ")"]
delimiters += [str(i) for i in xrange(10)]
regex_pattern = '|'.join(re.escape(_) for _ in delimiters)
diff -r 752db2db1ae0cba8e823303a5cc34d1b3a9c6ad0 -r 9a4b5263deaa33b9bd2ea07834c1d6d6daf45740 yt/units/unit_lookup_table.py
--- a/yt/units/unit_lookup_table.py
+++ b/yt/units/unit_lookup_table.py
@@ -138,10 +138,6 @@
if key not in latex_symbol_lut:
latex_symbol_lut[key] = "\\rm{" + key + "}"
-latex_prefixes = {
- "u" : "\\mu",
-}
-
# This dictionary formatting from magnitude package, credit to Juan Reyero.
unit_prefixes = {
'Y': 1e24, # yotta
@@ -164,6 +160,10 @@
'y': 1e-24, # yocto
}
+latex_prefixes = {
+ "u" : "\\mu",
+ }
+
prefixable_units = (
"m",
"pc",
https://bitbucket.org/yt_analysis/yt/commits/cb4b5f56b749/
Changeset: cb4b5f56b749
Branch: yt
User: jzuhone
Date: 2014-10-28 23:37:02+00:00
Summary: Adding sum method for off-axis projections
Affected #: 3 files
diff -r 9a4b5263deaa33b9bd2ea07834c1d6d6daf45740 -r cb4b5f56b749d8ac76fa8f7b53a855f5f1390d4b yt/visualization/fixed_resolution.py
--- a/yt/visualization/fixed_resolution.py
+++ b/yt/visualization/fixed_resolution.py
@@ -418,7 +418,7 @@
width, dd.resolution, item,
weight=dd.weight_field, volume=dd.volume,
no_ghost=dd.no_ghost, interpolated=dd.interpolated,
- north_vector=dd.north_vector)
+ north_vector=dd.north_vector, method=dd.method)
units = Unit(dd.ds.field_info[item].units, registry=dd.ds.unit_registry)
if dd.weight_field is None:
units *= Unit('cm', registry=dd.ds.unit_registry)
diff -r 9a4b5263deaa33b9bd2ea07834c1d6d6daf45740 -r cb4b5f56b749d8ac76fa8f7b53a855f5f1390d4b yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -1304,12 +1304,11 @@
class OffAxisProjectionDummyDataSource(object):
_type_name = 'proj'
- method = 'integrate'
_key_fields = []
def __init__(self, center, ds, normal_vector, width, fields,
interpolated, resolution = (800,800), weight=None,
volume=None, no_ghost=False, le=None, re=None,
- north_vector=None):
+ north_vector=None, method="integrate"):
self.center = center
self.ds = ds
self.axis = 4 # always true for oblique data objects
@@ -1326,6 +1325,7 @@
self.le = le
self.re = re
self.north_vector = north_vector
+ self.method = method
def _determine_fields(self, *args):
return self.dd._determine_fields(*args)
@@ -1396,7 +1396,18 @@
set, an arbitrary grid-aligned north-vector is chosen.
fontsize : integer
The size of the fonts for the axis, colorbar, and tick labels.
+ method : string
+ The method of projection. Valid methods are:
+ "integrate" with no weight_field specified : integrate the requested
+ field along the line of sight.
+
+ "integrate" with a weight_field specified : weight the requested
+ field by the weighting field and integrate along the line of sight.
+
+ "sum" : This method is the same as integrate, except that it does not
+ multiply by a path length when performing the integration, and is
+ just a straight summation of the field along the given axis.
"""
_plot_type = 'OffAxisProjection'
_frb_generator = OffAxisProjectionFixedResolutionBuffer
@@ -1404,7 +1415,7 @@
def __init__(self, ds, normal, fields, center='c', width=None,
depth=(1, '1'), axes_unit=None, weight_field=None,
max_level=None, north_vector=None, volume=None, no_ghost=False,
- le=None, re=None, interpolated=False, fontsize=18):
+ le=None, re=None, interpolated=False, fontsize=18, method="integrate"):
(bounds, center_rot) = \
get_oblique_window_parameters(normal,center,width,ds,depth=depth)
fields = ensure_list(fields)[:]
@@ -1414,7 +1425,7 @@
OffAxisProj = OffAxisProjectionDummyDataSource(
center_rot, ds, normal, oap_width, fields, interpolated,
weight=weight_field, volume=volume, no_ghost=no_ghost,
- le=le, re=re, north_vector=north_vector)
+ le=le, re=re, north_vector=north_vector, method=method)
# If a non-weighted, integral projection, assure field-label reflects that
if weight_field is None and OffAxisProj.method == "integrate":
self.projected = True
diff -r 9a4b5263deaa33b9bd2ea07834c1d6d6daf45740 -r cb4b5f56b749d8ac76fa8f7b53a855f5f1390d4b yt/visualization/volume_rendering/camera.py
--- a/yt/visualization/volume_rendering/camera.py
+++ b/yt/visualization/volume_rendering/camera.py
@@ -2161,7 +2161,8 @@
class ProjectionCamera(Camera):
def __init__(self, center, normal_vector, width, resolution,
field, weight=None, volume=None, no_ghost = False,
- north_vector=None, ds=None, interpolated=False):
+ north_vector=None, ds=None, interpolated=False,
+ method="integrate"):
if not interpolated:
volume = 1
@@ -2170,6 +2171,7 @@
self.field = field
self.weight = weight
self.resolution = resolution
+ self.method = method
fields = [field]
if self.weight is not None:
@@ -2240,11 +2242,12 @@
dd = ds.all_data()
field = dd._determine_fields([self.field])[0]
finfo = ds._get_field_info(*field)
- if self.weight is None:
- dl = self.width[2]
- image *= dl
- else:
- image[:,:,0] /= image[:,:,1]
+ if self.method == "integrate":
+ if self.weight is None:
+ dl = self.width[2]
+ image *= dl
+ else:
+ image[:,:,0] /= image[:,:,1]
return image[:,:,0]
@@ -2332,7 +2335,7 @@
def off_axis_projection(ds, center, normal_vector, width, resolution,
field, weight = None,
volume = None, no_ghost = False, interpolated = False,
- north_vector = None):
+ north_vector = None, method = "integrate"):
r"""Project through a dataset, off-axis, and return the image plane.
This function will accept the necessary items to integrate through a volume
@@ -2376,6 +2379,18 @@
If True, the data is first interpolated to vertex-centered data,
then tri-linearly interpolated along the ray. Not suggested for
quantitative studies.
+ method : string
+ The method of projection. Valid methods are:
+
+ "integrate" with no weight_field specified : integrate the requested
+ field along the line of sight.
+
+ "integrate" with a weight_field specified : weight the requested
+ field by the weighting field and integrate along the line of sight.
+
+ "sum" : This method is the same as integrate, except that it does not
+ multiply by a path length when performing the integration, and is
+ just a straight summation of the field along the given axis.
Returns
-------
@@ -2393,7 +2408,7 @@
projcam = ProjectionCamera(center, normal_vector, width, resolution,
field, weight=weight, ds=ds, volume=volume,
no_ghost=no_ghost, interpolated=interpolated,
- north_vector=north_vector)
+ north_vector=north_vector, method=method)
image = projcam.snapshot()
return image[:,:]
https://bitbucket.org/yt_analysis/yt/commits/4add1016c9d2/
Changeset: 4add1016c9d2
Branch: yt
User: jzuhone
Date: 2014-10-28 23:38:39+00:00
Summary: Make sure we use the same random seed for this test every time
Affected #: 1 file
diff -r cb4b5f56b749d8ac76fa8f7b53a855f5f1390d4b -r 4add1016c9d2e2097cf91458a96c0108434b53eb yt/analysis_modules/ppv_cube/tests/test_ppv.py
--- a/yt/analysis_modules/ppv_cube/tests/test_ppv.py
+++ b/yt/analysis_modules/ppv_cube/tests/test_ppv.py
@@ -24,6 +24,8 @@
def test_ppv():
+ np.random.seed(seed=0x4d3d3d3)
+
dims = (8,8,1024)
v_shift = 1.0e7*u.cm/u.s
sigma_v = 2.0e7*u.cm/u.s
https://bitbucket.org/yt_analysis/yt/commits/13c7bfbb36e7/
Changeset: 13c7bfbb36e7
Branch: yt
User: jzuhone
Date: 2014-10-28 23:47:21+00:00
Summary: Allow for integrate or sum projection methods.
Affected #: 1 file
diff -r 4add1016c9d2e2097cf91458a96c0108434b53eb -r 13c7bfbb36e7b67d1e4dd9d13e865bb26faef054 yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -47,7 +47,7 @@
class PPVCube(object):
def __init__(self, ds, normal, field, center="c", width=(1.0,"unitary"),
dims=(100,100,100), velocity_bounds=None, thermal_broad=False,
- atomic_weight=56.):
+ atomic_weight=56., method="integrate"):
r""" Initialize a PPVCube object.
Parameters
@@ -74,6 +74,10 @@
atomic_weight : float, optional
Set this value to the atomic weight of the particle that is emitting the line
if *thermal_broad* is True. Defaults to 56 (Fe).
+ method : string, optional
+ Set the projection method to be used.
+ "integrate" : line of sight integration over the line element.
+ "sum" : straight summation over the line of sight.
Examples
--------
@@ -95,6 +99,10 @@
self.ny = dims[1]
self.nv = dims[2]
+ if method not in ["integrate","sum"]:
+ raise RuntimeError("Only the 'integrate' and 'sum' projection +"
+ "methods are supported in PPVCube.")
+
dd = ds.all_data()
fd = dd._determine_fields(field)[0]
@@ -123,7 +131,10 @@
_intensity = self.create_intensity()
self.ds.add_field(("gas","intensity"), function=_intensity, units=self.field_units)
- self.proj_units = str(ds.quan(1.0, self.field_units+"*cm").units)
+ if method == "integrate":
+ self.proj_units = str(ds.quan(1.0, self.field_units+"*cm").units)
+ elif method == "sum":
+ self.proj_units = self.field_units
self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.proj_units)
storage = {}
@@ -131,11 +142,12 @@
for sto, i in parallel_objects(xrange(self.nv), storage=storage):
self.current_v = self.vmid_cgs[i]
if isinstance(normal, basestring):
- prj = ds.proj("intensity", ds.coordinates.axis_id[normal])
+ prj = ds.proj("intensity", ds.coordinates.axis_id[normal], method=method)
buf = prj.to_frb(width, self.nx, center=self.center)["intensity"]
else:
buf = off_axis_projection(ds, self.center, normal, width,
- (self.nx, self.ny), "intensity", no_ghost=True)[::-1]
+ (self.nx, self.ny), "intensity",
+ no_ghost=True, method=method)[::-1]
sto.result_id = i
sto.result = buf
pbar.update(i)
https://bitbucket.org/yt_analysis/yt/commits/edd198e1dbcc/
Changeset: edd198e1dbcc
Branch: yt
User: jzuhone
Date: 2014-10-31 15:36:41+00:00
Summary: More options for specifying resolution of image, and more general way of determining nx, ny, dx, dy
Affected #: 1 file
diff -r 13c7bfbb36e7b67d1e4dd9d13e865bb26faef054 -r edd198e1dbcc6f0459add7e5f7a58914eabbea55 yt/utilities/fits_image.py
--- a/yt/utilities/fits_image.py
+++ b/yt/utilities/fits_image.py
@@ -258,7 +258,7 @@
unit = "AU"
return unit
-def construct_image(data_source, center=None, width=None):
+def construct_image(data_source, center=None, width=None, image_res=None):
ds = data_source.ds
axis = data_source.axis
if center is None or width is None:
@@ -269,8 +269,18 @@
"so setting the center to the domain center.")
else:
width = ds.coordinates.sanitize_width(axis, width, None)
- dx = ds.index.get_smallest_dx()
- nx, ny = [int((w/dx).in_units("dimensionless")) for w in width]
+ if image_res is None:
+ dd = ds.all_data()
+ dx, dy = [dd.quantities.extrema("d%s" % "xyz"[idx])[0]
+ for idx in axis_wcs[axis]]
+ nx = int(width[0]/dx).in_units("dimensionless")
+ ny = int(width[1]/dy).in_units("dimensionless")
+ else:
+ if iterable(image_res):
+ nx, ny = image_res
+ else:
+ nx, ny = image_res, image_res
+ dx, dy = width[0]/nx, width[1]/ny
crpix = [0.5*(nx+1), 0.5*(ny+1)]
if hasattr(ds, "wcs"):
# This is a FITS dataset, so we use it to construct the WCS
@@ -326,7 +336,8 @@
axis = fix_axis(axis, ds)
center, dcenter = ds.coordinates.sanitize_center(center, axis)
slc = ds.slice(axis, center[axis], **kwargs)
- w, frb = construct_image(slc, center=dcenter, width=width)
+ w, frb = construct_image(slc, center=dcenter, width=width,
+ image_res=image_res)
super(FITSSlice, self).__init__(frb, fields=fields, wcs=w)
for i, field in enumerate(fields):
self[i].header["bunit"] = str(frb[field].units)
@@ -356,12 +367,13 @@
width :
"""
def __init__(self, ds, axis, fields, center="c", width=None,
- weight_field=None, **kwargs):
+ weight_field=None, image_res=None, **kwargs):
fields = ensure_list(fields)
axis = fix_axis(axis, ds)
center, dcenter = ds.coordinates.sanitize_center(center, axis)
prj = ds.proj(fields[0], axis, weight_field=weight_field, **kwargs)
- w, frb = construct_image(prj, center=dcenter, width=width)
+ w, frb = construct_image(prj, center=dcenter, width=width,
+ image_res=image_res)
super(FITSProjection, self).__init__(frb, fields=fields, wcs=w)
for i, field in enumerate(fields):
self[i].header["bunit"] = str(frb[field].units)
https://bitbucket.org/yt_analysis/yt/commits/b44d52f2e9bf/
Changeset: b44d52f2e9bf
Branch: yt
User: jzuhone
Date: 2014-10-31 15:40:01+00:00
Summary: Docstring improvements
Affected #: 1 file
diff -r edd198e1dbcc6f0459add7e5f7a58914eabbea55 -r b44d52f2e9bff6f9782c921bc5689dcc04fc74fa yt/utilities/fits_image.py
--- a/yt/utilities/fits_image.py
+++ b/yt/utilities/fits_image.py
@@ -329,7 +329,29 @@
as a tuple containing a coordinate and string unit name or by passing
in a YTArray. If a list or unitless array is supplied, code units are
assumed.
- width :
+ width : tuple or a float.
+ Width can have four different formats to support windows with variable
+ x and y widths. They are:
+
+ ================================== =======================
+ format example
+ ================================== =======================
+ (float, string) (10,'kpc')
+ ((float, string), (float, string)) ((10,'kpc'),(15,'kpc'))
+ float 0.2
+ (float, float) (0.2, 0.3)
+ ================================== =======================
+
+ For example, (10, 'kpc') requests a plot window that is 10 kiloparsecs
+ wide in the x and y directions, ((10,'kpc'),(15,'kpc')) requests a
+ window that is 10 kiloparsecs wide along the x axis and 15
+ kiloparsecs wide along the y axis. In the other two examples, code
+ units are assumed, for example (0.2, 0.3) requests a plot that has an
+ x width of 0.2 and a y width of 0.3 in code units. If units are
+ provided the resulting plot axis labels will use the supplied units.
+ image_res : an int or 2-tuple of ints
+ Specify the resolution of the resulting image. If not provided, it will be
+ determined based on the minimum cell size of the dataset.
"""
def __init__(self, ds, axis, fields, center="c", width=None, **kwargs):
fields = ensure_list(fields)
@@ -364,7 +386,29 @@
as a tuple containing a coordinate and string unit name or by passing
in a YTArray. If a list or unitless array is supplied, code units are
assumed.
- width :
+ width : tuple or a float.
+ Width can have four different formats to support windows with variable
+ x and y widths. They are:
+
+ ================================== =======================
+ format example
+ ================================== =======================
+ (float, string) (10,'kpc')
+ ((float, string), (float, string)) ((10,'kpc'),(15,'kpc'))
+ float 0.2
+ (float, float) (0.2, 0.3)
+ ================================== =======================
+
+ For example, (10, 'kpc') requests a plot window that is 10 kiloparsecs
+ wide in the x and y directions, ((10,'kpc'),(15,'kpc')) requests a
+ window that is 10 kiloparsecs wide along the x axis and 15
+ kiloparsecs wide along the y axis. In the other two examples, code
+ units are assumed, for example (0.2, 0.3) requests a plot that has an
+ x width of 0.2 and a y width of 0.3 in code units. If units are
+ provided the resulting plot axis labels will use the supplied units.
+ image_res : an int or 2-tuple of ints
+ Specify the resolution of the resulting image. If not provided, it will be
+ determined based on the minimum cell size of the dataset.
"""
def __init__(self, ds, axis, fields, center="c", width=None,
weight_field=None, image_res=None, **kwargs):
https://bitbucket.org/yt_analysis/yt/commits/64fab868c335/
Changeset: 64fab868c335
Branch: yt
User: jzuhone
Date: 2014-10-31 15:42:32+00:00
Summary: Bugfix
Affected #: 1 file
diff -r b44d52f2e9bff6f9782c921bc5689dcc04fc74fa -r 64fab868c335a36008544aea17e840d493f9a259 yt/utilities/fits_image.py
--- a/yt/utilities/fits_image.py
+++ b/yt/utilities/fits_image.py
@@ -273,8 +273,8 @@
dd = ds.all_data()
dx, dy = [dd.quantities.extrema("d%s" % "xyz"[idx])[0]
for idx in axis_wcs[axis]]
- nx = int(width[0]/dx).in_units("dimensionless")
- ny = int(width[1]/dy).in_units("dimensionless")
+ nx = int((width[0]/dx).in_units("dimensionless"))
+ ny = int((width[1]/dy).in_units("dimensionless"))
else:
if iterable(image_res):
nx, ny = image_res
https://bitbucket.org/yt_analysis/yt/commits/c7af5e4ddcdf/
Changeset: c7af5e4ddcdf
Branch: yt
User: jzuhone
Date: 2014-10-31 20:13:08+00:00
Summary: Min/max of any variable for sanitze_center
Affected #: 1 file
diff -r c91388a959906a04a5db1e773537e94bcb636a62 -r c7af5e4ddcdfb579a5d8b370ef48ef5f5f52f019 yt/geometry/coordinates/coordinate_handler.py
--- a/yt/geometry/coordinates/coordinate_handler.py
+++ b/yt/geometry/coordinates/coordinate_handler.py
@@ -183,7 +183,15 @@
elif isinstance(center, YTArray):
return self.ds.arr(center), self.convert_to_cartesian(center)
elif iterable(center):
- if iterable(center[0]) and isinstance(center[1], basestring):
+ if isinstance(center[0], basestring) and isinstance(center[1], basestring):
+ if center[0].lower() == "min":
+ v, center = self.ds.find_min(center[1])
+ elif center[0].lower() == "max":
+ v, center = self.ds.find_max(center[1])
+ else:
+ raise RuntimeError("center keyword \"%s\" not recognized" % center)
+ center = self.ds.arr(center, 'code_length')
+ elif iterable(center[0]) and isinstance(center[1], basestring):
center = self.ds.arr(center[0], center[1])
else:
center = self.ds.arr(center, 'code_length')
https://bitbucket.org/yt_analysis/yt/commits/d0b8664501d0/
Changeset: d0b8664501d0
Branch: yt
User: jzuhone
Date: 2014-10-31 20:13:30+00:00
Summary: Merge
Affected #: 17 files
diff -r c7af5e4ddcdfb579a5d8b370ef48ef5f5f52f019 -r d0b8664501d0d314d4c2a020a330dbb724e0e9e1 doc/source/analyzing/analysis_modules/PPVCube.ipynb
--- a/doc/source/analyzing/analysis_modules/PPVCube.ipynb
+++ b/doc/source/analyzing/analysis_modules/PPVCube.ipynb
@@ -1,7 +1,7 @@
{
"metadata": {
"name": "",
- "signature": "sha256:56a8d72735e3cc428ff04b241d4b2ce6f653019818c6fc7a4148840d99030c85"
+ "signature": "sha256:b83e125278c2e58da4d99ac9d2ca2a136d01f1094e1b83497925e0f9b9b056c2"
},
"nbformat": 3,
"nbformat_minor": 0,
@@ -32,7 +32,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "To demonstrate this functionality, we'll create a simple unigrid dataset from scratch of a rotating disk galaxy. We create a thin disk in the x-y midplane of the domain of three cells in height in either direction, and a radius of 10 kpc. The density and azimuthal velocity profiles of the disk as a function of radius will be given by the following functions:"
+ "To demonstrate this functionality, we'll create a simple unigrid dataset from scratch of a rotating disk. We create a thin disk in the x-y midplane of the domain of three cells in height in either direction, and a radius of 10 kpc. The density and azimuthal velocity profiles of the disk as a function of radius will be given by the following functions:"
]
},
{
@@ -84,7 +84,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Second, we'll construct the data arrays for the density and the velocity of the disk. Since we have the tangential velocity profile, we have to use the polar coordinates we derived earlier to compute `velx` and `vely`. Everywhere outside the disk, all fields are set to zero. "
+ "Second, we'll construct the data arrays for the density, temperature, and velocity of the disk. Since we have the tangential velocity profile, we have to use the polar coordinates we derived earlier to compute `velx` and `vely`. Everywhere outside the disk, all fields are set to zero. "
]
},
{
@@ -93,12 +93,15 @@
"input": [
"dens = np.zeros((nx,ny,nz))\n",
"dens[:,:,nz/2-3:nz/2+3] = (r**alpha).reshape(nx,ny,1) # the density profile of the disk\n",
- "vel_theta = r/(1.+(r/r_0)**beta) # the azimuthal velocity profile of the disk\n",
+ "temp = np.zeros((nx,ny,nz))\n",
+ "temp[:,:,nz/2-3:nz/2+3] = 1.0e5 # Isothermal\n",
+ "vel_theta = 100.*r/(1.+(r/r_0)**beta) # the azimuthal velocity profile of the disk\n",
"velx = np.zeros((nx,ny,nz))\n",
"vely = np.zeros((nx,ny,nz))\n",
"velx[:,:,nz/2-3:nz/2+3] = (-vel_theta*np.sin(theta)).reshape(nx,ny,1) # convert polar to cartesian\n",
"vely[:,:,nz/2-3:nz/2+3] = (vel_theta*np.cos(theta)).reshape(nx,ny,1) # convert polar to cartesian\n",
"dens[r > R] = 0.0\n",
+ "temp[r > R] = 0.0\n",
"velx[r > R] = 0.0\n",
"vely[r > R] = 0.0"
],
@@ -119,6 +122,7 @@
"input": [
"data = {}\n",
"data[\"density\"] = (dens,\"g/cm**3\")\n",
+ "data[\"temperature\"] = (temp, \"K\")\n",
"data[\"velocity_x\"] = (velx, \"km/s\")\n",
"data[\"velocity_y\"] = (vely, \"km/s\")\n",
"data[\"velocity_z\"] = (np.zeros((nx,ny,nz)), \"km/s\") # zero velocity in the z-direction\n",
@@ -189,7 +193,7 @@
"cell_type": "code",
"collapsed": false,
"input": [
- "cube = PPVCube(ds, L, \"density\", dims=(200,100,50), velocity_bounds=(-1.5,1.5,\"km/s\"))"
+ "cube = PPVCube(ds, L, \"density\", dims=(200,100,50), velocity_bounds=(-150.,150.,\"km/s\"))"
],
"language": "python",
"metadata": {},
@@ -199,14 +203,33 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Following this, we can now write this cube to a FITS file:"
+ "Following this, we can now write this cube to a FITS file. The x and y axes of the file can be in length units, which can be optionally specified by `length_unit`:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
- "cube.write_fits(\"cube.fits\", clobber=True, length_unit=(5.0,\"deg\"))"
+ "cube.write_fits(\"cube.fits\", clobber=True, length_unit=\"kpc\")"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Or one can use the `sky_scale` and `sky_center` keywords to set up the coordinates in RA and Dec:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "sky_scale = (1.0, \"arcsec/kpc\")\n",
+ "sky_center = (30., 45.) # RA, Dec in degrees\n",
+ "cube.write_fits(\"cube_sky.fits\", clobber=True, sky_scale=sky_scale, sky_center=sky_center)"
],
"language": "python",
"metadata": {},
@@ -223,7 +246,7 @@
"cell_type": "code",
"collapsed": false,
"input": [
- "ds = yt.load(\"cube.fits\")"
+ "ds_cube = yt.load(\"cube.fits\")"
],
"language": "python",
"metadata": {},
@@ -234,7 +257,7 @@
"collapsed": false,
"input": [
"# Specifying no center gives us the center slice\n",
- "slc = yt.SlicePlot(ds, \"z\", [\"density\"])\n",
+ "slc = yt.SlicePlot(ds_cube, \"z\", [\"density\"])\n",
"slc.show()"
],
"language": "python",
@@ -247,9 +270,9 @@
"input": [
"import yt.units as u\n",
"# Picking different velocities for the slices\n",
- "new_center = ds.domain_center\n",
- "new_center[2] = ds.spec2pixel(-1.0*u.km/u.s)\n",
- "slc = yt.SlicePlot(ds, \"z\", [\"density\"], center=new_center)\n",
+ "new_center = ds_cube.domain_center\n",
+ "new_center[2] = ds_cube.spec2pixel(-100.*u.km/u.s)\n",
+ "slc = yt.SlicePlot(ds_cube, \"z\", [\"density\"], center=new_center)\n",
"slc.show()"
],
"language": "python",
@@ -260,8 +283,8 @@
"cell_type": "code",
"collapsed": false,
"input": [
- "new_center[2] = ds.spec2pixel(0.7*u.km/u.s)\n",
- "slc = yt.SlicePlot(ds, \"z\", [\"density\"], center=new_center)\n",
+ "new_center[2] = ds_cube.spec2pixel(70.0*u.km/u.s)\n",
+ "slc = yt.SlicePlot(ds_cube, \"z\", [\"density\"], center=new_center)\n",
"slc.show()"
],
"language": "python",
@@ -272,8 +295,8 @@
"cell_type": "code",
"collapsed": false,
"input": [
- "new_center[2] = ds.spec2pixel(-0.3*u.km/u.s)\n",
- "slc = yt.SlicePlot(ds, \"z\", [\"density\"], center=new_center)\n",
+ "new_center[2] = ds_cube.spec2pixel(-30.0*u.km/u.s)\n",
+ "slc = yt.SlicePlot(ds_cube, \"z\", [\"density\"], center=new_center)\n",
"slc.show()"
],
"language": "python",
@@ -291,7 +314,7 @@
"cell_type": "code",
"collapsed": false,
"input": [
- "prj = yt.ProjectionPlot(ds, \"z\", [\"density\"], method=\"sum\")\n",
+ "prj = yt.ProjectionPlot(ds_cube, \"z\", [\"density\"], method=\"sum\")\n",
"prj.set_log(\"density\", True)\n",
"prj.set_zlim(\"density\", 1.0e-3, 0.2)\n",
"prj.show()"
@@ -299,9 +322,100 @@
"language": "python",
"metadata": {},
"outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The `thermal_broad` keyword allows one to simulate thermal line broadening based on the temperature, and the `atomic_weight` argument is used to specify the atomic weight of the particle that is doing the emitting."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "cube2 = PPVCube(ds, L, \"density\", dims=(200,100,50), velocity_bounds=(-150,150,\"km/s\"), thermal_broad=True, \n",
+ " atomic_weight=12.0)\n",
+ "cube2.write_fits(\"cube2.fits\", clobber=True, length_unit=\"kpc\")"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Taking a slice of this cube shows:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "ds_cube2 = yt.load(\"cube2.fits\")\n",
+ "new_center = ds_cube2.domain_center\n",
+ "new_center[2] = ds_cube2.spec2pixel(70.0*u.km/u.s)\n",
+ "slc = yt.SlicePlot(ds_cube2, \"z\", [\"density\"], center=new_center)\n",
+ "slc.show()"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "new_center[2] = ds_cube2.spec2pixel(-100.*u.km/u.s)\n",
+ "slc = yt.SlicePlot(ds_cube2, \"z\", [\"density\"], center=new_center)\n",
+ "slc.show()"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "where we can see the emission has been smeared into this velocity slice from neighboring slices due to the thermal broadening. \n",
+ "\n",
+ "Finally, the \"velocity\" or \"spectral\" axis of the cube can be changed to a different unit, such as wavelength, frequency, or energy: "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "print cube2.vbins[0], cube2.vbins[-1]\n",
+ "cube2.transform_spectral_axis(400.0,\"nm\")\n",
+ "print cube2.vbins[0], cube2.vbins[-1]"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If a FITS file is now written from the cube, the spectral axis will be in the new units. To reset the spectral axis back to the original velocity units:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "cube2.reset_spectral_axis()\n",
+ "print cube2.vbins[0], cube2.vbins[-1]"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
}
],
"metadata": {}
}
]
-}
+}
\ No newline at end of file
diff -r c7af5e4ddcdfb579a5d8b370ef48ef5f5f52f019 -r d0b8664501d0d314d4c2a020a330dbb724e0e9e1 yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -13,31 +13,55 @@
import numpy as np
from yt.utilities.on_demand_imports import _astropy
from yt.utilities.orientation import Orientation
-from yt.utilities.fits_image import FITSImageBuffer
+from yt.utilities.fits_image import FITSImageBuffer, sanitize_fits_unit
from yt.visualization.volume_rendering.camera import off_axis_projection
from yt.funcs import get_pbar
+from yt.utilities.physical_constants import clight, mh
+import yt.units.dimensions as ytdims
+from yt.funcs import iterable
+from yt.utilities.parallel_tools.parallel_analysis_interface import \
+ parallel_root_only, parallel_objects
+import re
+import ppv_utils
+from yt.funcs import is_root
-def create_vlos(z_hat):
- def _v_los(field, data):
- vz = data["velocity_x"]*z_hat[0] + \
- data["velocity_y"]*z_hat[1] + \
- data["velocity_z"]*z_hat[2]
- return -vz
+def create_vlos(normal):
+ if isinstance(normal, basestring):
+ def _v_los(field, data):
+ return -data["velocity_%s" % normal]
+ else:
+ orient = Orientation(normal)
+ los_vec = orient.unit_vectors[2]
+ def _v_los(field, data):
+ vz = data["velocity_x"]*los_vec[0] + \
+ data["velocity_y"]*los_vec[1] + \
+ data["velocity_z"]*los_vec[2]
+ return -vz
return _v_los
+fits_info = {"velocity":("m/s","VELOCITY","v"),
+ "frequency":("Hz","FREQUENCY","f"),
+ "energy":("eV","ENERGY","E"),
+ "wavelength":("angstrom","WAVELENG","lambda")}
+
class PPVCube(object):
- def __init__(self, ds, normal, field, width=(1.0,"unitary"),
- dims=(100,100,100), velocity_bounds=None):
+ def __init__(self, ds, normal, field, center="c", width=(1.0,"unitary"),
+ dims=(100,100,100), velocity_bounds=None, thermal_broad=False,
+ atomic_weight=56., method="integrate"):
r""" Initialize a PPVCube object.
Parameters
----------
ds : dataset
The dataset.
- normal : array_like
- The normal vector along with to make the projections.
+ normal : array_like or string
+ The normal vector along with to make the projections. If an array, it
+ will be normalized. If a string, it will be assumed to be along one of the
+ principal axes of the domain ("x","y", or "z").
field : string
The field to project.
+ center : float, tuple, or string
+ The coordinates of the dataset *ds* on which to center the PPVCube.
width : float or tuple, optional
The width of the projection in length units. Specify a float
for code_length units or a tuple (value, units).
@@ -47,6 +71,13 @@
A 3-tuple of (vmin, vmax, units) for the velocity bounds to
integrate over. If None, the largest velocity of the
dataset will be used, e.g. velocity_bounds = (-v.max(), v.max())
+ atomic_weight : float, optional
+ Set this value to the atomic weight of the particle that is emitting the line
+ if *thermal_broad* is True. Defaults to 56 (Fe).
+ method : string, optional
+ Set the projection method to be used.
+ "integrate" : line of sight integration over the line element.
+ "sum" : straight summation over the line of sight.
Examples
--------
@@ -55,21 +86,22 @@
>>> cube = PPVCube(ds, L, "density", width=(10.,"kpc"),
... velocity_bounds=(-5.,4.,"km/s"))
"""
+
self.ds = ds
self.field = field
self.width = width
+ self.particle_mass = atomic_weight*mh
+ self.thermal_broad = thermal_broad
+
+ self.center = ds.coordinates.sanitize_center(center, normal)[0]
self.nx = dims[0]
self.ny = dims[1]
self.nv = dims[2]
- normal = np.array(normal)
- normal /= np.sqrt(np.dot(normal, normal))
- vecs = np.identity(3)
- t = np.cross(normal, vecs).sum(axis=1)
- ax = t.argmax()
- north = np.cross(normal, vecs[ax,:]).ravel()
- orient = Orientation(normal, north_vector=north)
+ if method not in ["integrate","sum"]:
+ raise RuntimeError("Only the 'integrate' and 'sum' projection +"
+ "methods are supported in PPVCube.")
dd = ds.all_data()
@@ -85,27 +117,102 @@
ds.quan(velocity_bounds[1], velocity_bounds[2]))
self.vbins = np.linspace(self.v_bnd[0], self.v_bnd[1], num=self.nv+1)
+ self._vbins = self.vbins.copy()
self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
- self.dv = (self.v_bnd[1]-self.v_bnd[0])/self.nv
+ self.vmid_cgs = self.vmid.in_cgs().v
+ self.dv = self.vbins[1]-self.vbins[0]
+ self.dv_cgs = self.dv.in_cgs().v
- _vlos = create_vlos(orient.unit_vectors[2])
- ds.field_info.add_field(("gas","v_los"), function=_vlos, units="cm/s")
+ self.current_v = 0.0
- self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.field_units)
+ _vlos = create_vlos(normal)
+ self.ds.add_field(("gas","v_los"), function=_vlos, units="cm/s")
+
+ _intensity = self.create_intensity()
+ self.ds.add_field(("gas","intensity"), function=_intensity, units=self.field_units)
+
+ if method == "integrate":
+ self.proj_units = str(ds.quan(1.0, self.field_units+"*cm").units)
+ elif method == "sum":
+ self.proj_units = self.field_units
+
+ self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.proj_units)
+ storage = {}
pbar = get_pbar("Generating cube.", self.nv)
- for i in xrange(self.nv):
- _intensity = self._create_intensity(i)
- ds.add_field(("gas","intensity"), function=_intensity, units=self.field_units)
- prj = off_axis_projection(ds, ds.domain_center, normal, width,
- (self.nx, self.ny), "intensity")
- self.data[:,:,i] = prj[:,:]
- ds.field_info.pop(("gas","intensity"))
+ for sto, i in parallel_objects(xrange(self.nv), storage=storage):
+ self.current_v = self.vmid_cgs[i]
+ if isinstance(normal, basestring):
+ prj = ds.proj("intensity", ds.coordinates.axis_id[normal], method=method)
+ buf = prj.to_frb(width, self.nx, center=self.center)["intensity"]
+ else:
+ buf = off_axis_projection(ds, self.center, normal, width,
+ (self.nx, self.ny), "intensity",
+ no_ghost=True, method=method)[::-1]
+ sto.result_id = i
+ sto.result = buf
pbar.update(i)
-
pbar.finish()
- def write_fits(self, filename, clobber=True, length_unit=(10.0, "kpc"),
- sky_center=(30.,45.)):
+ self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.proj_units)
+ if is_root():
+ for i, buf in sorted(storage.items()):
+ self.data[:,:,i] = buf[:,:]
+
+ self.axis_type = "velocity"
+
+ # Now fix the width
+ if iterable(self.width):
+ self.width = ds.quan(self.width[0], self.width[1])
+ else:
+ self.width = ds.quan(self.width, "code_length")
+
+ def create_intensity(self):
+ def _intensity(field, data):
+ v = self.current_v-data["v_los"].v
+ T = data["temperature"].v
+ w = ppv_utils.compute_weight(self.thermal_broad, self.dv_cgs,
+ self.particle_mass, v.flatten(), T.flatten())
+ w[np.isnan(w)] = 0.0
+ return data[self.field]*w.reshape(v.shape)
+ return _intensity
+
+ def transform_spectral_axis(self, rest_value, units):
+ """
+ Change the units of the spectral axis to some equivalent unit, such
+ as energy, wavelength, or frequency, by providing a *rest_value* and the
+ *units* of the new spectral axis. This corresponds to the Doppler-shifting
+ of lines due to gas motions and thermal broadening.
+ """
+ if self.axis_type != "velocity":
+ self.reset_spectral_axis()
+ x0 = self.ds.quan(rest_value, units)
+ if x0.units.dimensions == ytdims.rate or x0.units.dimensions == ytdims.energy:
+ self.vbins = x0*(1.-self.vbins.in_cgs()/clight)
+ elif x0.units.dimensions == ytdims.length:
+ self.vbins = x0/(1.-self.vbins.in_cgs()/clight)
+ self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
+ self.dv = self.vbins[1]-self.vbins[0]
+ dims = self.dv.units.dimensions
+ if dims == ytdims.rate:
+ self.axis_type = "frequency"
+ elif dims == ytdims.length:
+ self.axis_type = "wavelength"
+ elif dims == ytdims.energy:
+ self.axis_type = "energy"
+ elif dims == ytdims.velocity:
+ self.axis_type = "velocity"
+
+ def reset_spectral_axis(self):
+ """
+ Reset the spectral axis to the original velocity range and units.
+ """
+ self.vbins = self._vbins.copy()
+ self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
+ self.dv = self.vbins[1]-self.vbins[0]
+
+ @parallel_root_only
+ def write_fits(self, filename, clobber=True, length_unit=None,
+ sky_scale=None, sky_center=None):
r""" Write the PPVCube to a FITS file.
Parameters
@@ -114,51 +221,71 @@
The name of the file to write.
clobber : boolean
Whether or not to clobber an existing file with the same name.
- length_unit : tuple, optional
- The length that corresponds to the width of the projection in
- (value, unit) form. Accepts a length unit or 'deg'.
+ length_unit : string
+ The units to convert the coordinates to in the file.
+ sky_scale : tuple or YTQuantity
+ Conversion between an angle unit and a length unit, if sky
+ coordinates are desired.
+ Examples: (1.0, "arcsec/kpc"), YTQuantity(0.001, "deg/kpc")
sky_center : tuple, optional
The (RA, Dec) coordinate in degrees of the central pixel if
- *length_unit* is 'deg'.
+ *sky_scale* has been specified.
Examples
--------
- >>> cube.write_fits("my_cube.fits", clobber=False, length_unit=(5,"deg"))
+ >>> cube.write_fits("my_cube.fits", clobber=False, sky_scale=(1.0,"arcsec/kpc"))
"""
- if length_unit[1] == "deg":
- center = sky_center
- types = ["RA---SIN","DEC--SIN"]
+ if sky_scale is None:
+ center = (0.0,0.0)
+ types = ["LINEAR","LINEAR"]
else:
- center = [0.0,0.0]
- types = ["LINEAR","LINEAR"]
+ if iterable(sky_scale):
+ sky_scale = self.ds.quan(sky_scale[0], sky_scale[1])
+ if sky_center is None:
+ center = (30.,45.)
+ else:
+ center = sky_center
+ types = ["RA---TAN","DEC--TAN"]
- v_center = 0.5*(self.v_bnd[0]+self.v_bnd[1]).in_units("m/s").value
+ vunit = fits_info[self.axis_type][0]
+ vtype = fits_info[self.axis_type][1]
- dx = length_unit[0]/self.nx
- dy = length_unit[0]/self.ny
- dv = self.dv.in_units("m/s").value
+ v_center = 0.5*(self.vbins[0]+self.vbins[-1]).in_units(vunit).value
- if length_unit[1] == "deg":
+ if sky_scale:
+ dx = (self.width*sky_scale).in_units("deg").v/self.nx
+ units = "deg"
+ else:
+ if length_unit is None:
+ units = str(self.ds.get_smallest_appropriate_unit(self.width))
+ else:
+ units = length_unit
+ units = sanitize_fits_unit(units)
+ dx = self.width.in_units(units).v/self.nx
+ dy = dx
+ dv = self.dv.in_units(vunit).v
+
+ if sky_scale:
dx *= -1.
w = _astropy.pywcs.WCS(naxis=3)
w.wcs.crpix = [0.5*(self.nx+1), 0.5*(self.ny+1), 0.5*(self.nv+1)]
w.wcs.cdelt = [dx,dy,dv]
- w.wcs.crval = [center[0], center[1], v_center]
- w.wcs.cunit = [length_unit[1],length_unit[1],"m/s"]
- w.wcs.ctype = [types[0],types[1],"VELO-LSR"]
+ w.wcs.crval = [center[0],center[1],v_center]
+ w.wcs.cunit = [units,units,vunit]
+ w.wcs.ctype = [types[0],types[1],vtype]
fib = FITSImageBuffer(self.data.transpose(), fields=self.field, wcs=w)
- fib[0].header["bunit"] = self.field_units
+ fib[0].header["bunit"] = re.sub('()', '', str(self.proj_units))
fib[0].header["btype"] = self.field
fib.writeto(filename, clobber=clobber)
- def _create_intensity(self, i):
- def _intensity(field, data):
- vlos = data["v_los"]
- w = np.abs(vlos-self.vmid[i])/self.dv.in_units(vlos.units)
- w = 1.-w
- w[w < 0.0] = 0.0
- return data[self.field]*w
- return _intensity
+ def __repr__(self):
+ return "PPVCube [%d %d %d] (%s < %s < %s)" % (self.nx, self.ny, self.nv,
+ self.vbins[0],
+ fits_info[self.axis_type][2],
+ self.vbins[-1])
+
+ def __getitem__(self, item):
+ return self.data[item]
diff -r c7af5e4ddcdfb579a5d8b370ef48ef5f5f52f019 -r d0b8664501d0d314d4c2a020a330dbb724e0e9e1 yt/analysis_modules/ppv_cube/ppv_utils.pyx
--- /dev/null
+++ b/yt/analysis_modules/ppv_cube/ppv_utils.pyx
@@ -0,0 +1,40 @@
+import numpy as np
+cimport numpy as np
+cimport cython
+from yt.utilities.physical_constants import kboltz
+
+cdef extern from "math.h":
+ double exp(double x) nogil
+ double fabs(double x) nogil
+ double sqrt(double x) nogil
+
+cdef double kb = kboltz.v
+cdef double pi = np.pi
+
+ at cython.cdivision(True)
+ at cython.boundscheck(False)
+ at cython.wraparound(False)
+def compute_weight(np.uint8_t thermal_broad,
+ double dv,
+ double m_part,
+ np.ndarray[np.float64_t, ndim=1] v,
+ np.ndarray[np.float64_t, ndim=1] T):
+
+ cdef int i, n
+ cdef double v2_th, x
+ cdef np.ndarray[np.float64_t, ndim=1] w
+
+ n = v.shape[0]
+ w = np.zeros(n)
+
+ for i in range(n):
+ if thermal_broad:
+ if T[i] > 0.0:
+ v2_th = 2.*kb*T[i]/m_part
+ w[i] = dv*exp(-v[i]*v[i]/v2_th)/sqrt(v2_th*pi)
+ else:
+ x = 1.-fabs(v[i])/dv
+ if x > 0.0:
+ w[i] = x
+
+ return w
diff -r c7af5e4ddcdfb579a5d8b370ef48ef5f5f52f019 -r d0b8664501d0d314d4c2a020a330dbb724e0e9e1 yt/analysis_modules/ppv_cube/setup.py
--- a/yt/analysis_modules/ppv_cube/setup.py
+++ b/yt/analysis_modules/ppv_cube/setup.py
@@ -8,7 +8,10 @@
def configuration(parent_package='', top_path=None):
from numpy.distutils.misc_util import Configuration
config = Configuration('ppv_cube', parent_package, top_path)
- #config.add_subpackage("tests")
+ config.add_extension("ppv_utils",
+ ["yt/analysis_modules/ppv_cube/ppv_utils.pyx"],
+ libraries=["m"])
+ config.add_subpackage("tests")
config.make_config_py() # installs __config__.py
#config.make_svn_version_py()
return config
diff -r c7af5e4ddcdfb579a5d8b370ef48ef5f5f52f019 -r d0b8664501d0d314d4c2a020a330dbb724e0e9e1 yt/analysis_modules/ppv_cube/tests/test_ppv.py
--- /dev/null
+++ b/yt/analysis_modules/ppv_cube/tests/test_ppv.py
@@ -0,0 +1,62 @@
+"""
+Unit test the sunyaev_zeldovich analysis module.
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2013, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+from yt.frontends.stream.api import load_uniform_grid
+from yt.analysis_modules.ppv_cube.api import PPVCube
+import yt.units as u
+from yt.utilities.physical_constants import kboltz, mh, clight
+import numpy as np
+from yt.testing import *
+
+def setup():
+ """Test specific setup."""
+ from yt.config import ytcfg
+ ytcfg["yt", "__withintesting"] = "True"
+
+def test_ppv():
+
+ np.random.seed(seed=0x4d3d3d3)
+
+ dims = (8,8,1024)
+ v_shift = 1.0e7*u.cm/u.s
+ sigma_v = 2.0e7*u.cm/u.s
+ T_0 = 1.0e8*u.Kelvin
+ data = {"density":(np.ones(dims),"g/cm**3"),
+ "temperature":(T_0.v*np.ones(dims), "K"),
+ "velocity_x":(np.zeros(dims),"cm/s"),
+ "velocity_y":(np.zeros(dims),"cm/s"),
+ "velocity_z":(np.random.normal(loc=v_shift.v,scale=sigma_v.v,size=dims), "cm/s")}
+
+ ds = load_uniform_grid(data, dims)
+
+ cube = PPVCube(ds, "z", "density", dims=dims,
+ velocity_bounds=(-300., 300., "km/s"),
+ thermal_broad=True)
+
+ dv = cube.dv
+ v_th = np.sqrt(2.*kboltz*T_0/(56.*mh) + 2.*sigma_v**2).in_units("km/s")
+ a = cube.data.mean(axis=(0,1)).v
+ b = dv*np.exp(-((cube.vmid+v_shift)/v_th)**2)/(np.sqrt(np.pi)*v_th)
+
+ yield assert_allclose, a, b, 1.0e-2
+
+ E_0 = 6.8*u.keV
+
+ cube.transform_spectral_axis(E_0.v, str(E_0.units))
+
+ dE = -cube.dv
+ delta_E = E_0*v_th.in_cgs()/clight
+ E_shift = E_0*(1.+v_shift/clight)
+
+ c = dE*np.exp(-((cube.vmid-E_shift)/delta_E)**2)/(np.sqrt(np.pi)*delta_E)
+
+ yield assert_allclose, a, c, 1.0e-2
diff -r c7af5e4ddcdfb579a5d8b370ef48ef5f5f52f019 -r d0b8664501d0d314d4c2a020a330dbb724e0e9e1 yt/analysis_modules/sunyaev_zeldovich/projection.py
--- a/yt/analysis_modules/sunyaev_zeldovich/projection.py
+++ b/yt/analysis_modules/sunyaev_zeldovich/projection.py
@@ -25,6 +25,7 @@
from yt.utilities.parallel_tools.parallel_analysis_interface import \
communication_system, parallel_root_only
from yt import units
+from yt.utilities.on_demand_imports import _astropy
import numpy as np
@@ -279,8 +280,7 @@
self.data["TeSZ"] = self.ds.arr(Te, "keV")
@parallel_root_only
- def write_fits(self, filename, units="kpc", sky_center=None, sky_scale=None,
- time_units="Gyr", clobber=True):
+ def write_fits(self, filename, sky_scale=None, sky_center=None, clobber=True):
r""" Export images to a FITS file. Writes the SZ distortion in all
specified frequencies as well as the mass-weighted temperature and the
optical depth. Distance units are in kpc, unless *sky_center*
@@ -290,12 +290,13 @@
----------
filename : string
The name of the FITS file to be written.
- sky_center : tuple of floats, optional
- The center of the observation in (RA, Dec) in degrees. Only used if
- converting to sky coordinates.
- sky_scale : float, optional
- Scale between degrees and kpc. Only used if
- converting to sky coordinates.
+ sky_scale : tuple or YTQuantity
+ Conversion between an angle unit and a length unit, if sky
+ coordinates are desired.
+ Examples: (1.0, "arcsec/kpc"), YTQuantity(0.001, "deg/kpc")
+ sky_center : tuple, optional
+ The (RA, Dec) coordinate in degrees of the central pixel if
+ *sky_scale* has been specified.
clobber : boolean, optional
If the file already exists, do we overwrite?
@@ -304,27 +305,43 @@
>>> # This example just writes out a FITS file with kpc coords
>>> szprj.write_fits("SZbullet.fits", clobber=False)
>>> # This example uses sky coords
- >>> sky_scale = 1./3600. # One arcsec per kpc
+ >>> sky_scale = (1., "arcsec/kpc") # One arcsec per kpc
>>> sky_center = (30., 45.) # In degrees
>>> szprj.write_fits("SZbullet.fits", sky_center=sky_center, sky_scale=sky_scale)
"""
+ from yt.utilities.fits_image import FITSImageBuffer, sanitize_fits_unit
- deltas = np.array([self.dx.in_units(units),
- self.dy.in_units(units)])
+ if sky_scale is None:
+ center = (0.0,0.0)
+ types = ["LINEAR","LINEAR"]
+ else:
+ if iterable(sky_scale):
+ sky_scale = self.ds.quan(sky_scale[0], sky_scale[1])
+ if sky_center is None:
+ center = (30.,45.)
+ else:
+ center = sky_center
+ types = ["RA---TAN","DEC--TAN"]
- if sky_center is None:
- center = [0.0]*2
- else:
- center = sky_center
+ units = self.ds.get_smallest_appropriate_unit(self.width)
+ units = sanitize_fits_unit(units)
+ # Hack because FITS is stupid and doesn't understand case
+ dx = self.dx.in_units(units)
+ if sky_scale:
+ dx = (dx*sky_scale).in_units("deg")
units = "deg"
- deltas *= sky_scale
- deltas[0] *= -1.
+ dy = dx
+ if sky_scale:
+ dx *= -1.
- from yt.utilities.fits_image import FITSImageBuffer
- fib = FITSImageBuffer(self.data, fields=self.data.keys(),
- center=center, units=units,
- scale=deltas)
- fib.update_all_headers("Time", float(self.ds.current_time.in_units(time_units).value))
+ w = _astropy.pywcs.WCS(naxis=2)
+ w.wcs.crpix = [0.5*(self.nx+1)]*2
+ w.wcs.cdelt = [dx.v,dy.v]
+ w.wcs.crval = center
+ w.wcs.cunit = [units]*2
+ w.wcs.ctype = types
+
+ fib = FITSImageBuffer(self.data, fields=self.data.keys(), wcs=w)
fib.writeto(filename, clobber=clobber)
@parallel_root_only
diff -r c7af5e4ddcdfb579a5d8b370ef48ef5f5f52f019 -r d0b8664501d0d314d4c2a020a330dbb724e0e9e1 yt/frontends/fits/data_structures.py
--- a/yt/frontends/fits/data_structures.py
+++ b/yt/frontends/fits/data_structures.py
@@ -44,9 +44,9 @@
from yt.units.yt_array import YTQuantity
from yt.utilities.on_demand_imports import _astropy
-lon_prefixes = ["X","RA","GLON"]
-lat_prefixes = ["Y","DEC","GLAT"]
-delimiters = ["*", "/", "-", "^"]
+lon_prefixes = ["X","RA","GLON","LINEAR"]
+lat_prefixes = ["Y","DEC","GLAT","LINEAR"]
+delimiters = ["*", "/", "-", "^", "(", ")"]
delimiters += [str(i) for i in xrange(10)]
regex_pattern = '|'.join(re.escape(_) for _ in delimiters)
@@ -552,7 +552,7 @@
x = 0
for p in lon_prefixes+lat_prefixes+spec_names.keys():
y = np_char.startswith(self.axis_names[:self.dimensionality], p)
- x += y.sum()
+ x += np.any(y)
if x == self.dimensionality: self._setup_spec_cube()
def _setup_spec_cube(self):
@@ -582,6 +582,12 @@
self.lon_axis = np.where(self.lon_axis)[0][0]
self.lon_name = ctypes[self.lon_axis].split("-")[0].lower()
+ if self.lat_axis == self.lon_axis and self.lat_name == self.lon_name:
+ self.lat_axis = 1
+ self.lon_axis = 0
+ self.lat_name = "Y"
+ self.lon_name = "X"
+
if self.wcs.naxis > 2:
self.spec_axis = np.zeros((end-1), dtype="bool")
diff -r c7af5e4ddcdfb579a5d8b370ef48ef5f5f52f019 -r d0b8664501d0d314d4c2a020a330dbb724e0e9e1 yt/geometry/coordinates/spec_cube_coordinates.py
--- a/yt/geometry/coordinates/spec_cube_coordinates.py
+++ b/yt/geometry/coordinates/spec_cube_coordinates.py
@@ -26,8 +26,19 @@
self.axis_name = {}
self.axis_id = {}
- for axis, axis_name in zip([ds.lon_axis, ds.lat_axis, ds.spec_axis],
- ["Image\ x", "Image\ y", ds.spec_name]):
+ self.default_unit_label = {}
+ if ds.lon_name == "X" and ds.lat_name == "Y":
+ names = ["x","y"]
+ else:
+ names = ["Image\ x", "Image\ y"]
+ self.default_unit_label[ds.lon_axis] = "pixel"
+ self.default_unit_label[ds.lat_axis] = "pixel"
+ names.append(ds.spec_name)
+ axes = [ds.lon_axis, ds.lat_axis, ds.spec_axis]
+ self.default_unit_label[ds.spec_axis] = ds.spec_unit
+
+ for axis, axis_name in zip(axes, names):
+
lower_ax = "xyz"[axis]
upper_ax = lower_ax.upper()
@@ -40,11 +51,6 @@
self.axis_id[axis] = axis
self.axis_id[axis_name] = axis
- self.default_unit_label = {}
- self.default_unit_label[ds.lon_axis] = "pixel"
- self.default_unit_label[ds.lat_axis] = "pixel"
- self.default_unit_label[ds.spec_axis] = ds.spec_unit
-
def _spec_axis(ax, x, y):
p = (x,y)[ax]
return [self.ds.pixel2spec(pp).v for pp in p]
diff -r c7af5e4ddcdfb579a5d8b370ef48ef5f5f52f019 -r d0b8664501d0d314d4c2a020a330dbb724e0e9e1 yt/units/unit_lookup_table.py
--- a/yt/units/unit_lookup_table.py
+++ b/yt/units/unit_lookup_table.py
@@ -160,6 +160,10 @@
'y': 1e-24, # yocto
}
+latex_prefixes = {
+ "u" : "\\mu",
+ }
+
prefixable_units = (
"m",
"pc",
@@ -177,6 +181,7 @@
"Hz",
"W",
"gauss",
+ "G",
"Jy",
)
diff -r c7af5e4ddcdfb579a5d8b370ef48ef5f5f52f019 -r d0b8664501d0d314d4c2a020a330dbb724e0e9e1 yt/units/unit_object.py
--- a/yt/units/unit_object.py
+++ b/yt/units/unit_object.py
@@ -26,7 +26,7 @@
from yt.units.unit_lookup_table import \
latex_symbol_lut, unit_prefixes, \
prefixable_units, cgs_base_units, \
- mks_base_units
+ mks_base_units, latex_prefixes
from yt.units.unit_registry import UnitRegistry
import copy
@@ -512,9 +512,14 @@
prefix_value = unit_prefixes[possible_prefix]
if symbol_str not in latex_symbol_lut:
+ if possible_prefix in latex_prefixes:
+ sstr = symbol_str.replace(possible_prefix,
+ '{'+latex_prefixes[possible_prefix]+'}')
+ else:
+ sstr = symbol_str
latex_symbol_lut[symbol_str] = \
latex_symbol_lut[symbol_wo_prefix].replace(
- '{'+symbol_wo_prefix+'}', '{'+symbol_str+'}')
+ '{'+symbol_wo_prefix+'}', '{'+sstr+'}')
# don't forget to account for the prefix value!
return (unit_data[0] * prefix_value, unit_data[1])
diff -r c7af5e4ddcdfb579a5d8b370ef48ef5f5f52f019 -r d0b8664501d0d314d4c2a020a330dbb724e0e9e1 yt/utilities/fits_image.py
--- a/yt/utilities/fits_image.py
+++ b/yt/utilities/fits_image.py
@@ -15,7 +15,8 @@
from yt.visualization.fixed_resolution import FixedResolutionBuffer
from yt.data_objects.construction_data_containers import YTCoveringGridBase
from yt.utilities.on_demand_imports import _astropy
-from yt.units.yt_array import YTQuantity
+from yt.units.yt_array import YTQuantity, YTArray
+import re
pyfits = _astropy.pyfits
pywcs = _astropy.pywcs
@@ -27,8 +28,7 @@
class FITSImageBuffer(HDUList):
- def __init__(self, data, fields=None, units="cm",
- center=None, scale=None, wcs=None):
+ def __init__(self, data, fields=None, units="cm", wcs=None):
r""" Initialize a FITSImageBuffer object.
FITSImageBuffer contains a list of FITS ImageHDU instances, and
@@ -49,29 +49,32 @@
keys, it will use these for the fields. If *data* is just a
single array one field name must be specified.
units : string
- The units of the WCS coordinates, default "cm".
- center : array_like, optional
- The coordinates [xctr,yctr] of the images in units
- *units*. If *units* is not specified, defaults to the origin.
- scale : tuple of floats, optional
- Pixel scale in unit *units*. Will be ignored if *data* is
- a FixedResolutionBuffer or a YTCoveringGrid. Must be
- specified otherwise, or if *units* is "deg".
+ The units of the WCS coordinates. Only applies
+ to FixedResolutionBuffers or YTCoveringGrids. Defaults to "cm".
wcs : `astropy.wcs.WCS` instance, optional
- Supply an AstroPy WCS instance to override automatic WCS creation.
+ Supply an AstroPy WCS instance. Will override automatic WCS
+ creation from FixedResolutionBuffers and YTCoveringGrids.
Examples
--------
+ >>> # This example uses a FRB.
>>> ds = load("sloshing_nomag2_hdf5_plt_cnt_0150")
>>> prj = ds.proj(2, "kT", weight_field="density")
>>> frb = prj.to_frb((0.5, "Mpc"), 800)
>>> # This example just uses the FRB and puts the coords in kpc.
>>> f_kpc = FITSImageBuffer(frb, fields="kT", units="kpc")
- >>> # This example specifies sky coordinates.
- >>> scale = [1./3600.]*2 # One arcsec per pixel
- >>> f_deg = FITSImageBuffer(frb, fields="kT", units="deg",
- scale=scale, center=(30., 45.))
+ >>> # This example specifies a specific WCS.
+ >>> from astropy.wcs import WCS
+ >>> w = WCS(naxis=self.dimensionality)
+ >>> w.wcs.crval = [30., 45.] # RA, Dec in degrees
+ >>> w.wcs.cunit = ["deg"]*2
+ >>> nx, ny = 800, 800
+ >>> w.wcs.crpix = [0.5*(nx+1), 0.5*(ny+1)]
+ >>> w.wcs.ctype = ["RA---TAN","DEC--TAN"]
+ >>> scale = 1./3600. # One arcsec per pixel
+ >>> w.wcs.cdelt = [-scale, scale]
+ >>> f_deg = FITSImageBuffer(frb, fields="kT", wcs=w)
>>> f_deg.writeto("temp.fits")
"""
@@ -98,8 +101,14 @@
first = True
+ self.field_units = {}
+
for key in fields:
if key not in exclude_fields:
+ if hasattr(img_data[key], "units"):
+ self.field_units[key] = str(img_data[key].units)
+ else:
+ self.field_units[key] = "dimensionless"
mylog.info("Making a FITS image of field %s" % (key))
if first:
hdu = pyfits.PrimaryHDU(np.array(img_data[key]))
@@ -109,7 +118,7 @@
hdu.name = key
hdu.header["btype"] = key
if hasattr(img_data[key], "units"):
- hdu.header["bunit"] = str(img_data[key].units)
+ hdu.header["bunit"] = re.sub('()', '', str(img_data[key].units))
self.append(hdu)
self.dimensionality = len(self[0].data.shape)
@@ -121,25 +130,11 @@
has_coords = (isinstance(img_data, FixedResolutionBuffer) or
isinstance(img_data, YTCoveringGridBase))
-
- if center is None:
- if units == "deg":
- mylog.error("Please specify center=(RA, Dec) in degrees.")
- raise ValueError
- elif not has_coords:
- mylog.warning("Setting center to the origin.")
- center = [0.0]*self.dimensionality
-
- if scale is None:
- if units == "deg" or not has_coords and wcs is None:
- mylog.error("Please specify scale=(dx,dy[,dz]) in %s." % (units))
- raise ValueError
if wcs is None:
w = pywcs.WCS(header=self[0].header, naxis=self.dimensionality)
w.wcs.crpix = 0.5*(np.array(self.shape)+1)
- proj_type = ["linear"]*self.dimensionality
- if isinstance(img_data, FixedResolutionBuffer) and units != "deg":
+ if isinstance(img_data, FixedResolutionBuffer):
# FRBs are a special case where we have coordinate
# information, so we take advantage of this and
# construct the WCS object
@@ -151,28 +146,20 @@
elif isinstance(img_data, YTCoveringGridBase):
dx, dy, dz = img_data.dds.in_units(units)
center = 0.5*(img_data.left_edge+img_data.right_edge).in_units(units)
- elif units == "deg" and self.dimensionality == 2:
- dx = -scale[0]
- dy = scale[1]
- proj_type = ["RA---TAN","DEC--TAN"]
else:
- dx = scale[0]
- dy = scale[1]
- if self.dimensionality == 3: dz = scale[2]
-
+ # We default to pixel coordinates if nothing is provided
+ dx, dy, dz = 1.0, 1.0, 1.0
+ center = 0.5*(np.array(self.shape)+1)
w.wcs.crval = center
- w.wcs.cunit = [units]*self.dimensionality
- w.wcs.ctype = proj_type
-
+ if has_coords:
+ w.wcs.cunit = [units]*self.dimensionality
if self.dimensionality == 2:
w.wcs.cdelt = [dx,dy]
elif self.dimensionality == 3:
w.wcs.cdelt = [dx,dy,dz]
-
+ w.wcs.ctype = ["linear"]*self.dimensionality
self._set_wcs(w)
-
else:
-
self._set_wcs(wcs)
def _set_wcs(self, wcs):
@@ -194,7 +181,7 @@
for img in self: img.header[key] = value
def keys(self):
- return [f.name for f in self]
+ return [f.name.lower() for f in self]
def has_key(self, key):
return key in self.keys()
@@ -249,33 +236,71 @@
import aplpy
return aplpy.FITSFigure(self, **kwargs)
+ def get_data(self, field):
+ return YTArray(self[field].data, self.field_units[field])
+
+ def set_unit(self, field, units):
+ """
+ Set the units of *field* to *units*.
+ """
+ new_data = YTArray(self[field].data, self.field_units[field]).in_units(units)
+ self[field].data = new_data.v
+ self[field].header["bunit"] = units
+ self.field_units[field] = units
+
axis_wcs = [[1,2],[0,2],[0,1]]
-def construct_image(data_source, center=None):
+def sanitize_fits_unit(unit):
+ if unit == "Mpc":
+ mylog.info("Changing FITS file unit to kpc.")
+ unit = "kpc"
+ elif unit == "au":
+ unit = "AU"
+ return unit
+
+def construct_image(data_source, center=None, width=None, image_res=None):
ds = data_source.ds
axis = data_source.axis
+ if center is None or width is None:
+ center = ds.domain_center[axis_wcs[axis]]
+ if width is None:
+ width = ds.domain_width[axis_wcs[axis]]
+ mylog.info("Making an image of the entire domain, "+
+ "so setting the center to the domain center.")
+ else:
+ width = ds.coordinates.sanitize_width(axis, width, None)
+ if image_res is None:
+ dd = ds.all_data()
+ dx, dy = [dd.quantities.extrema("d%s" % "xyz"[idx])[0]
+ for idx in axis_wcs[axis]]
+ nx = int((width[0]/dx).in_units("dimensionless"))
+ ny = int((width[1]/dy).in_units("dimensionless"))
+ else:
+ if iterable(image_res):
+ nx, ny = image_res
+ else:
+ nx, ny = image_res, image_res
+ dx, dy = width[0]/nx, width[1]/ny
+ crpix = [0.5*(nx+1), 0.5*(ny+1)]
if hasattr(ds, "wcs"):
- # This is a FITS dataset
- nx, ny = ds.domain_dimensions[axis_wcs[axis]]
- crpix = [ds.wcs.wcs.crpix[idx] for idx in axis_wcs[axis]]
- cdelt = [ds.wcs.wcs.cdelt[idx] for idx in axis_wcs[axis]]
- crval = [ds.wcs.wcs.crval[idx] for idx in axis_wcs[axis]]
+ # This is a FITS dataset, so we use it to construct the WCS
cunit = [str(ds.wcs.wcs.cunit[idx]) for idx in axis_wcs[axis]]
ctype = [ds.wcs.wcs.ctype[idx] for idx in axis_wcs[axis]]
+ cdelt = [ds.wcs.wcs.cdelt[idx] for idx in axis_wcs[axis]]
+ ctr_pix = center.in_units("code_length")[:self.dimensionality].v
+ crval = ds.wcs.wcs_pix2world(ctr_pix.reshape(1,self.dimensionality))[0]
+ crval = [crval[idx] for idx in axis_wcs[axis]]
else:
- # This is some other kind of dataset
- unit = ds.get_smallest_appropriate_unit(ds.domain_width.max())
- if center is None:
- crval = [0.0,0.0]
- else:
- crval = [(ds.domain_center-center)[idx].in_units(unit) for idx in axis_wcs[axis]]
- dx = ds.index.get_smallest_dx()
- nx, ny = (ds.domain_width[axis_wcs[axis]]/dx).ndarray_view().astype("int")
- crpix = [0.5*(nx+1), 0.5*(ny+1)]
- cdelt = [dx.in_units(unit)]*2
+ # This is some other kind of dataset
+ unit = str(width[0].units)
+ if unit == "unitary":
+ unit = ds.get_smallest_appropriate_unit(ds.domain_width.max())
+ unit = sanitize_fits_unit(unit)
cunit = [unit]*2
ctype = ["LINEAR"]*2
- frb = data_source.to_frb((1.0,"unitary"), (nx,ny))
+ cdelt = [dx.in_units(unit)]*2
+ crval = [center[idx].in_units(unit) for idx in axis_wcs[axis]]
+ frb = data_source.to_frb(width[0], (nx,ny), center=center, height=width[1])
w = pywcs.WCS(naxis=2)
w.wcs.crpix = crpix
w.wcs.cdelt = cdelt
@@ -304,13 +329,37 @@
as a tuple containing a coordinate and string unit name or by passing
in a YTArray. If a list or unitless array is supplied, code units are
assumed.
+ width : tuple or a float.
+ Width can have four different formats to support windows with variable
+ x and y widths. They are:
+
+ ================================== =======================
+ format example
+ ================================== =======================
+ (float, string) (10,'kpc')
+ ((float, string), (float, string)) ((10,'kpc'),(15,'kpc'))
+ float 0.2
+ (float, float) (0.2, 0.3)
+ ================================== =======================
+
+ For example, (10, 'kpc') requests a plot window that is 10 kiloparsecs
+ wide in the x and y directions, ((10,'kpc'),(15,'kpc')) requests a
+ window that is 10 kiloparsecs wide along the x axis and 15
+ kiloparsecs wide along the y axis. In the other two examples, code
+ units are assumed, for example (0.2, 0.3) requests a plot that has an
+ x width of 0.2 and a y width of 0.3 in code units. If units are
+ provided the resulting plot axis labels will use the supplied units.
+ image_res : an int or 2-tuple of ints
+ Specify the resolution of the resulting image. If not provided, it will be
+ determined based on the minimum cell size of the dataset.
"""
- def __init__(self, ds, axis, fields, center="c", **kwargs):
+ def __init__(self, ds, axis, fields, center="c", width=None, **kwargs):
fields = ensure_list(fields)
axis = fix_axis(axis, ds)
- center = ds.coordinates.sanitize_center(center, axis)
+ center, dcenter = ds.coordinates.sanitize_center(center, axis)
slc = ds.slice(axis, center[axis], **kwargs)
- w, frb = construct_image(slc, center=center)
+ w, frb = construct_image(slc, center=dcenter, width=width,
+ image_res=image_res)
super(FITSSlice, self).__init__(frb, fields=fields, wcs=w)
for i, field in enumerate(fields):
self[i].header["bunit"] = str(frb[field].units)
@@ -337,13 +386,38 @@
as a tuple containing a coordinate and string unit name or by passing
in a YTArray. If a list or unitless array is supplied, code units are
assumed.
+ width : tuple or a float.
+ Width can have four different formats to support windows with variable
+ x and y widths. They are:
+
+ ================================== =======================
+ format example
+ ================================== =======================
+ (float, string) (10,'kpc')
+ ((float, string), (float, string)) ((10,'kpc'),(15,'kpc'))
+ float 0.2
+ (float, float) (0.2, 0.3)
+ ================================== =======================
+
+ For example, (10, 'kpc') requests a plot window that is 10 kiloparsecs
+ wide in the x and y directions, ((10,'kpc'),(15,'kpc')) requests a
+ window that is 10 kiloparsecs wide along the x axis and 15
+ kiloparsecs wide along the y axis. In the other two examples, code
+ units are assumed, for example (0.2, 0.3) requests a plot that has an
+ x width of 0.2 and a y width of 0.3 in code units. If units are
+ provided the resulting plot axis labels will use the supplied units.
+ image_res : an int or 2-tuple of ints
+ Specify the resolution of the resulting image. If not provided, it will be
+ determined based on the minimum cell size of the dataset.
"""
- def __init__(self, ds, axis, fields, center="c", weight_field=None, **kwargs):
+ def __init__(self, ds, axis, fields, center="c", width=None,
+ weight_field=None, image_res=None, **kwargs):
fields = ensure_list(fields)
axis = fix_axis(axis, ds)
- center = ds.coordinates.sanitize_center(center, axis)
+ center, dcenter = ds.coordinates.sanitize_center(center, axis)
prj = ds.proj(fields[0], axis, weight_field=weight_field, **kwargs)
- w, frb = construct_image(prj, center=center)
+ w, frb = construct_image(prj, center=dcenter, width=width,
+ image_res=image_res)
super(FITSProjection, self).__init__(frb, fields=fields, wcs=w)
for i, field in enumerate(fields):
self[i].header["bunit"] = str(frb[field].units)
diff -r c7af5e4ddcdfb579a5d8b370ef48ef5f5f52f019 -r d0b8664501d0d314d4c2a020a330dbb724e0e9e1 yt/utilities/on_demand_imports.py
--- a/yt/utilities/on_demand_imports.py
+++ b/yt/utilities/on_demand_imports.py
@@ -132,4 +132,15 @@
self._interpolate = interpolate
return self._interpolate
+ _special = None
+ @property
+ def special(self):
+ if self._special is None:
+ try:
+ import scipy.special as special
+ except ImportError:
+ special = NotAModule(self._name)
+ self._special = special
+ return self._special
+
_scipy = scipy_imports()
\ No newline at end of file
diff -r c7af5e4ddcdfb579a5d8b370ef48ef5f5f52f019 -r d0b8664501d0d314d4c2a020a330dbb724e0e9e1 yt/visualization/fixed_resolution.py
--- a/yt/visualization/fixed_resolution.py
+++ b/yt/visualization/fixed_resolution.py
@@ -418,7 +418,7 @@
width, dd.resolution, item,
weight=dd.weight_field, volume=dd.volume,
no_ghost=dd.no_ghost, interpolated=dd.interpolated,
- north_vector=dd.north_vector)
+ north_vector=dd.north_vector, method=dd.method)
units = Unit(dd.ds.field_info[item].units, registry=dd.ds.unit_registry)
if dd.weight_field is None:
units *= Unit('cm', registry=dd.ds.unit_registry)
diff -r c7af5e4ddcdfb579a5d8b370ef48ef5f5f52f019 -r d0b8664501d0d314d4c2a020a330dbb724e0e9e1 yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -771,9 +771,10 @@
if hasattr(self.ds.coordinates, "default_unit_label"):
axax = getattr(self.ds.coordinates,
"%s_axis" % ("xy"[i]))[axis_index]
- un = self.ds.coordinates.default_unit_label[axax]
- axes_unit_labels[i] = '\/\/\left('+un+'\right)'
- continue
+ unn = self.ds.coordinates.default_unit_label.get(axax, "")
+ if unn != "":
+ axes_unit_labels[i] = '\/\/\left('+unn+'\right)'
+ continue
# Use sympy to factor h out of the unit. In this context 'un'
# is a string, so we call the Unit constructor.
expr = Unit(un, registry=self.ds.unit_registry).expr
@@ -1303,12 +1304,11 @@
class OffAxisProjectionDummyDataSource(object):
_type_name = 'proj'
- method = 'integrate'
_key_fields = []
def __init__(self, center, ds, normal_vector, width, fields,
interpolated, resolution = (800,800), weight=None,
volume=None, no_ghost=False, le=None, re=None,
- north_vector=None):
+ north_vector=None, method="integrate"):
self.center = center
self.ds = ds
self.axis = 4 # always true for oblique data objects
@@ -1325,6 +1325,7 @@
self.le = le
self.re = re
self.north_vector = north_vector
+ self.method = method
def _determine_fields(self, *args):
return self.dd._determine_fields(*args)
@@ -1395,7 +1396,18 @@
set, an arbitrary grid-aligned north-vector is chosen.
fontsize : integer
The size of the fonts for the axis, colorbar, and tick labels.
+ method : string
+ The method of projection. Valid methods are:
+ "integrate" with no weight_field specified : integrate the requested
+ field along the line of sight.
+
+ "integrate" with a weight_field specified : weight the requested
+ field by the weighting field and integrate along the line of sight.
+
+ "sum" : This method is the same as integrate, except that it does not
+ multiply by a path length when performing the integration, and is
+ just a straight summation of the field along the given axis.
"""
_plot_type = 'OffAxisProjection'
_frb_generator = OffAxisProjectionFixedResolutionBuffer
@@ -1403,7 +1415,7 @@
def __init__(self, ds, normal, fields, center='c', width=None,
depth=(1, '1'), axes_unit=None, weight_field=None,
max_level=None, north_vector=None, volume=None, no_ghost=False,
- le=None, re=None, interpolated=False, fontsize=18):
+ le=None, re=None, interpolated=False, fontsize=18, method="integrate"):
(bounds, center_rot) = \
get_oblique_window_parameters(normal,center,width,ds,depth=depth)
fields = ensure_list(fields)[:]
@@ -1413,7 +1425,7 @@
OffAxisProj = OffAxisProjectionDummyDataSource(
center_rot, ds, normal, oap_width, fields, interpolated,
weight=weight_field, volume=volume, no_ghost=no_ghost,
- le=le, re=re, north_vector=north_vector)
+ le=le, re=re, north_vector=north_vector, method=method)
# If a non-weighted, integral projection, assure field-label reflects that
if weight_field is None and OffAxisProj.method == "integrate":
self.projected = True
diff -r c7af5e4ddcdfb579a5d8b370ef48ef5f5f52f019 -r d0b8664501d0d314d4c2a020a330dbb724e0e9e1 yt/visualization/volume_rendering/camera.py
--- a/yt/visualization/volume_rendering/camera.py
+++ b/yt/visualization/volume_rendering/camera.py
@@ -2171,7 +2171,8 @@
class ProjectionCamera(Camera):
def __init__(self, center, normal_vector, width, resolution,
field, weight=None, volume=None, no_ghost = False,
- north_vector=None, ds=None, interpolated=False):
+ north_vector=None, ds=None, interpolated=False,
+ method="integrate"):
if not interpolated:
volume = 1
@@ -2180,6 +2181,7 @@
self.field = field
self.weight = weight
self.resolution = resolution
+ self.method = method
fields = [field]
if self.weight is not None:
@@ -2250,11 +2252,12 @@
dd = ds.all_data()
field = dd._determine_fields([self.field])[0]
finfo = ds._get_field_info(*field)
- if self.weight is None:
- dl = self.width[2]
- image *= dl
- else:
- image[:,:,0] /= image[:,:,1]
+ if self.method == "integrate":
+ if self.weight is None:
+ dl = self.width[2]
+ image *= dl
+ else:
+ image[:,:,0] /= image[:,:,1]
return image[:,:,0]
@@ -2342,7 +2345,7 @@
def off_axis_projection(ds, center, normal_vector, width, resolution,
field, weight = None,
volume = None, no_ghost = False, interpolated = False,
- north_vector = None):
+ north_vector = None, method = "integrate"):
r"""Project through a dataset, off-axis, and return the image plane.
This function will accept the necessary items to integrate through a volume
@@ -2386,6 +2389,18 @@
If True, the data is first interpolated to vertex-centered data,
then tri-linearly interpolated along the ray. Not suggested for
quantitative studies.
+ method : string
+ The method of projection. Valid methods are:
+
+ "integrate" with no weight_field specified : integrate the requested
+ field along the line of sight.
+
+ "integrate" with a weight_field specified : weight the requested
+ field by the weighting field and integrate along the line of sight.
+
+ "sum" : This method is the same as integrate, except that it does not
+ multiply by a path length when performing the integration, and is
+ just a straight summation of the field along the given axis.
Returns
-------
@@ -2403,7 +2418,7 @@
projcam = ProjectionCamera(center, normal_vector, width, resolution,
field, weight=weight, ds=ds, volume=volume,
no_ghost=no_ghost, interpolated=interpolated,
- north_vector=north_vector)
+ north_vector=north_vector, method=method)
image = projcam.snapshot()
return image[:,:]
diff -r c7af5e4ddcdfb579a5d8b370ef48ef5f5f52f019 -r d0b8664501d0d314d4c2a020a330dbb724e0e9e1 yt/visualization/volume_rendering/image_handling.py
--- a/yt/visualization/volume_rendering/image_handling.py
+++ b/yt/visualization/volume_rendering/image_handling.py
@@ -40,9 +40,7 @@
data["b"] = image[:,:,2]
data["a"] = image[:,:,3]
nx, ny = data["r"].shape
- fib = FITSImageBuffer(data, units="pixel",
- center=[0.5*(nx+1), 0.5*(ny+1)],
- scale=[1.]*2)
+ fib = FITSImageBuffer(data)
fib.writeto('%s.fits'%fn,clobber=True)
def import_rgba(name, h5=True):
https://bitbucket.org/yt_analysis/yt/commits/f99d8e3d1b45/
Changeset: f99d8e3d1b45
Branch: yt
User: jzuhone
Date: 2014-11-03 20:01:07+00:00
Summary: Getting units right for off-axis projections
Affected #: 1 file
diff -r d0b8664501d0d314d4c2a020a330dbb724e0e9e1 -r f99d8e3d1b45748ba0b6acfefcff74f68807ef08 yt/visualization/volume_rendering/camera.py
--- a/yt/visualization/volume_rendering/camera.py
+++ b/yt/visualization/volume_rendering/camera.py
@@ -2252,14 +2252,14 @@
dd = ds.all_data()
field = dd._determine_fields([self.field])[0]
finfo = ds._get_field_info(*field)
+ dl = 1.0
if self.method == "integrate":
if self.weight is None:
- dl = self.width[2]
- image *= dl
+ dl = self.width[2].in_units("cm")
else:
image[:,:,0] /= image[:,:,1]
- return image[:,:,0]
+ return ImageArray(image[:,:,0], finfo.units)*dl
def _render(self, double_check, num_threads, image, sampler):
https://bitbucket.org/yt_analysis/yt/commits/82e07ddca08e/
Changeset: 82e07ddca08e
Branch: yt
User: jzuhone
Date: 2014-11-03 20:10:38+00:00
Summary: Issue special warning when AstroPy isn't installed and it appears that we are trying to load a FITS file.
Affected #: 1 file
diff -r f99d8e3d1b45748ba0b6acfefcff74f68807ef08 -r 82e07ddca08e2a166fa2e6ed6caffefa24d32471 yt/frontends/fits/data_structures.py
--- a/yt/frontends/fits/data_structures.py
+++ b/yt/frontends/fits/data_structures.py
@@ -42,7 +42,7 @@
unit_prefixes
from yt.units import dimensions
from yt.units.yt_array import YTQuantity
-from yt.utilities.on_demand_imports import _astropy
+from yt.utilities.on_demand_imports import _astropy, NotAModule
lon_prefixes = ["X","RA","GLON","LINEAR"]
lat_prefixes = ["Y","DEC","GLAT","LINEAR"]
@@ -664,6 +664,8 @@
ext = args[0].rsplit(".", 1)[0].rsplit(".", 1)[-1]
if ext.upper() not in ("FITS", "FTS"):
return False
+ elif isinstance(_astropy.pyfits, NotAModule):
+ mylog.warning("This appears to be a FITS file, but AstroPy is not installed.")
try:
with warnings.catch_warnings():
warnings.filterwarnings('ignore', category=UserWarning, append=True)
https://bitbucket.org/yt_analysis/yt/commits/1b18659fba23/
Changeset: 1b18659fba23
Branch: yt
User: jzuhone
Date: 2014-11-03 20:19:10+00:00
Summary: Throw a RuntimeError instead.
Affected #: 1 file
diff -r 82e07ddca08e2a166fa2e6ed6caffefa24d32471 -r 1b18659fba23a51604134b163716a7cbb6f609e5 yt/frontends/fits/data_structures.py
--- a/yt/frontends/fits/data_structures.py
+++ b/yt/frontends/fits/data_structures.py
@@ -665,7 +665,7 @@
if ext.upper() not in ("FITS", "FTS"):
return False
elif isinstance(_astropy.pyfits, NotAModule):
- mylog.warning("This appears to be a FITS file, but AstroPy is not installed.")
+ raise RuntimeError("This appears to be a FITS file, but AstroPy is not installed.")
try:
with warnings.catch_warnings():
warnings.filterwarnings('ignore', category=UserWarning, append=True)
https://bitbucket.org/yt_analysis/yt/commits/3d3bdd8cbf98/
Changeset: 3d3bdd8cbf98
Branch: yt
User: jzuhone
Date: 2014-11-03 20:30:28+00:00
Summary: Merge
Affected #: 16 files
diff -r 1b18659fba23a51604134b163716a7cbb6f609e5 -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 doc/source/analyzing/units/2)_Fields_and_unit_conversion.ipynb
--- a/doc/source/analyzing/units/2)_Fields_and_unit_conversion.ipynb
+++ b/doc/source/analyzing/units/2)_Fields_and_unit_conversion.ipynb
@@ -1,7 +1,7 @@
{
"metadata": {
"name": "",
- "signature": "sha256:2faff88abc93fe2bc9d91467db786a8b69ec3ece6783a7055942ecc7c47a0817"
+ "signature": "sha256:c7cfb2db456d127bb633b7eee7ad6fe14290aa622ac62694c7840d80137afaba"
},
"nbformat": 3,
"nbformat_minor": 0,
@@ -36,7 +36,7 @@
"input": [
"import yt\n",
"ds = yt.load('IsolatedGalaxy/galaxy0030/galaxy0030')\n",
- " \n",
+ "\n",
"dd = ds.all_data()\n",
"maxval, maxloc = ds.find_max('density')\n",
"\n",
@@ -222,6 +222,69 @@
"level": 3,
"metadata": {},
"source": [
+ "Electrostatic/Electromagnetic Units"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Electromagnetic units can be a bit tricky, because the units for such quantities in different unit systems can have entirely different dimensions, even if they are meant to represent the same physical quantities. For example, in the SI system of units, current in Amperes is a fundamental unit of measure, so the unit of charge \"coulomb\" is equal to one ampere-second. On the other hand, in the Gaussian/CGS system, there is no equivalent base electromagnetic unit, and the electrostatic charge unit \"esu\" is equal to one $\\mathrm{cm^{3/2}g^{-1/2}s^{-1}}$ (which does not have any apparent physical significance). `yt` recognizes this difference:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "q1 = yt.YTArray(1.0,\"C\") # Coulombs\n",
+ "q2 = yt.YTArray(1.0,\"esu\") # electrostatic units / statcoulomb\n",
+ "\n",
+ "print \"units =\", q1.in_mks().units, \", dims =\", q1.units.dimensions\n",
+ "print \"units =\", q2.in_cgs().units, \", dims =\", q2.units.dimensions"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Under the hood, the `yt` units system has a translation layer that converts between these two systems, without any further effort required. For example:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "from yt.utilities.physical_constants import elementary_charge\n",
+ "\n",
+ "print elementary_charge\n",
+ "elementary_charge_C = elementary_charge.in_units(\"C\")\n",
+ "print elementary_charge_C"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The electromagnetic unit translations `yt` understands are:\n",
+ "\n",
+ "* Charge: 1 coulomb (C) $\\leftrightarrow$ 0.1c electrostatic unit (esu, Fr)\n",
+ "* Current: 1 ampere (A, C/s) $\\leftrightarrow$ 0.1c statampere (statA, esu/s, Fr) \n",
+ "* Magnetic Field: 1 tesla (T) $\\leftrightarrow 10^4$ gauss (G)\n",
+ "\n",
+ "where \"Fr\" is the franklin, an alternative name for the electrostatic unit, and c is the speed of light. "
+ ]
+ },
+ {
+ "cell_type": "heading",
+ "level": 3,
+ "metadata": {},
+ "source": [
"Working with views and converting to ndarray"
]
},
@@ -324,10 +387,9 @@
"collapsed": false,
"input": [
"from astropy import units as u\n",
- "from yt import YTQuantity, YTArray\n",
"\n",
"x = 42.0 * u.meter\n",
- "y = YTQuantity.from_astropy(x) "
+ "y = yt.YTQuantity.from_astropy(x) "
],
"language": "python",
"metadata": {},
@@ -349,7 +411,7 @@
"collapsed": false,
"input": [
"a = np.random.random(size=10) * u.km/u.s\n",
- "b = YTArray.from_astropy(a)"
+ "b = yt.YTArray.from_astropy(a)"
],
"language": "python",
"metadata": {},
@@ -436,7 +498,7 @@
"collapsed": false,
"input": [
"k1 = kboltz.to_astropy()\n",
- "k2 = YTQuantity.from_astropy(kb)\n",
+ "k2 = yt.YTQuantity.from_astropy(kb)\n",
"print k1 == k2"
],
"language": "python",
@@ -447,7 +509,7 @@
"cell_type": "code",
"collapsed": false,
"input": [
- "c = YTArray.from_astropy(a)\n",
+ "c = yt.YTArray.from_astropy(a)\n",
"d = c.to_astropy()\n",
"print a == d"
],
diff -r 1b18659fba23a51604134b163716a7cbb6f609e5 -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 doc/source/analyzing/units/6)_Unit_Equivalencies.ipynb
--- /dev/null
+++ b/doc/source/analyzing/units/6)_Unit_Equivalencies.ipynb
@@ -0,0 +1,179 @@
+{
+ "metadata": {
+ "name": "",
+ "signature": "sha256:cee652d703dd3369d81ebc670882d3734f73d0274aab98823a784d8039355480"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+ {
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Some physical quantities are directly related to other unitful quantities by a constant, but otherwise do not have the same units. To facilitate conversions between these quantities, `yt` implements a system of unit equivalencies (inspired by the [AstroPy implementation](http://docs.astropy.org/en/latest/units/equivalencies.html)). The possible unit equivalencies are:\n",
+ "\n",
+ "* `\"thermal\"`: conversions between temperature and energy ($E = k_BT$)\n",
+ "* `\"spectral\"`: conversions between wavelength, frequency, and energy for photons ($E = h\\nu = hc/\\lambda, c = \\lambda\\nu$)\n",
+ "* `\"mass_energy\"`: conversions between mass and energy ($E = mc^2$)\n",
+ "* `\"lorentz\"`: conversions between velocity and Lorentz factor ($\\gamma = 1/\\sqrt{1-(v/c)^2}$)\n",
+ "* `\"schwarzschild\"`: conversions between mass and Schwarzschild radius ($R_S = 2GM/c^2$)\n",
+ "* `\"compton\"`: conversions between mass and Compton wavelength ($\\lambda = h/mc$)\n",
+ "\n",
+ "The following unit equivalencies only apply under conditions applicable for an ideal gas with a constant mean molecular weight $\\mu$ and ratio of specific heats $\\gamma$:\n",
+ "\n",
+ "* `\"number_density\"`: conversions between density and number density ($n = \\rho/\\mu{m_p}$)\n",
+ "* `\"sound_speed\"`: conversions between temperature and sound speed for an ideal gas ($c_s^2 = \\gamma{k_BT}/\\mu{m_p}$)\n",
+ "\n",
+ "A `YTArray` or `YTQuantity` can be converted to an equivalent using `to_equivalent`, where the unit and the equivalence name are provided as arguments:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "import yt\n",
+ "import numpy as np\n",
+ "\n",
+ "ds = yt.load('IsolatedGalaxy/galaxy0030/galaxy0030')\n",
+ "\n",
+ "dd = ds.all_data()\n",
+ "\n",
+ "print dd[\"temperature\"].to_equivalent(\"erg\", \"thermal\")\n",
+ "print dd[\"temperature\"].to_equivalent(\"eV\", \"thermal\")\n",
+ "\n",
+ "# Rest energy of the proton\n",
+ "from yt.utilities.physical_constants import mp\n",
+ "E_p = mp.to_equivalent(\"GeV\", \"mass_energy\")\n",
+ "print E_p"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Equivalencies can go in both directions, without any information required other than the unit you want to convert to:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "from yt.utilities.physical_constants import clight\n",
+ "v = 0.1*clight\n",
+ "g = v.to_equivalent(\"dimensionless\", \"lorentz\")\n",
+ "print g\n",
+ "print g.to_equivalent(\"c\", \"lorentz\")"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "heading",
+ "level": 3,
+ "metadata": {},
+ "source": [
+ "Special Equivalencies"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Some equivalencies can take supplemental information. The `\"number_density\"` equivalence can take a custom mean molecular weight (default is $\\mu = 0.6$):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "print dd[\"density\"].max()\n",
+ "print dd[\"density\"].to_equivalent(\"cm**-3\", \"number_density\").max()\n",
+ "print dd[\"density\"].to_equivalent(\"cm**-3\", \"number_density\", mu=0.75).max()"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The `\"sound_speed\"` equivalence optionally takes the ratio of specific heats $\\gamma$ and the mean molecular weight $\\mu$ (defaults are $\\gamma$ = 5/3, $\\mu = 0.6$):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "print dd[\"temperature\"].to_equivalent(\"km/s\", \"sound_speed\").mean()\n",
+ "print dd[\"temperature\"].to_equivalent(\"km/s\", \"sound_speed\", gamma=4./3., mu=0.5).mean()"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "These options must be used with caution, and only if you know the underlying data adheres to these assumptions!"
+ ]
+ },
+ {
+ "cell_type": "heading",
+ "level": 3,
+ "metadata": {},
+ "source": [
+ "Determining Valid Equivalencies"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If a certain equivalence does not exist for a particular unit, then an error will be thrown:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "from yt.utilities.exceptions import YTInvalidUnitEquivalence\n",
+ "\n",
+ "try:\n",
+ " x = v.to_equivalent(\"angstrom\", \"spectral\")\n",
+ "except YTInvalidUnitEquivalence as e:\n",
+ " print e"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To list the equivalencies available for a given `YTArray` or `YTQuantity`, use the `list_equivalencies` method:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "E_p.list_equivalencies()"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ }
+ ],
+ "metadata": {}
+ }
+ ]
+}
\ No newline at end of file
diff -r 1b18659fba23a51604134b163716a7cbb6f609e5 -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 doc/source/analyzing/units/index.rst
--- a/doc/source/analyzing/units/index.rst
+++ b/doc/source/analyzing/units/index.rst
@@ -33,6 +33,7 @@
comoving_units_and_code_units
comparing_units_from_different_datasets
units_and_plotting
+ unit_equivalencies
.. note::
diff -r 1b18659fba23a51604134b163716a7cbb6f609e5 -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 doc/source/analyzing/units/unit_equivalencies.rst
--- /dev/null
+++ b/doc/source/analyzing/units/unit_equivalencies.rst
@@ -0,0 +1,7 @@
+.. _symbolic_units:
+
+Symbolic units: :code:`yt.units`
+================================
+
+.. notebook:: 6)_Unit_Equivalencies.ipynb
+ :skip_exceptions:
diff -r 1b18659fba23a51604134b163716a7cbb6f609e5 -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 yt/analysis_modules/photon_simulator/spectral_models.py
--- a/yt/analysis_modules/photon_simulator/spectral_models.py
+++ b/yt/analysis_modules/photon_simulator/spectral_models.py
@@ -39,7 +39,7 @@
self.emax = emax*units.keV
self.nchan = nchan
self.ebins = np.linspace(emin, emax, nchan+1)*units.keV
- self.de = np.diff(self.ebins)*units.keV
+ self.de = np.diff(self.ebins)
self.emid = 0.5*(self.ebins[1:]+self.ebins[:-1])
def prepare(self):
diff -r 1b18659fba23a51604134b163716a7cbb6f609e5 -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 yt/fields/astro_fields.py
--- a/yt/fields/astro_fields.py
+++ b/yt/fields/astro_fields.py
@@ -102,6 +102,15 @@
function=_xray_emissivity,
units="") # add correct units here
+ def _mazzotta_weighting(field, data):
+ # Spectroscopic-like weighting field for galaxy clusters
+ # Only useful as a weight_field for temperature, metallicity, velocity
+ return data["density"]*data["density"]*data["kT"]**-0.25/mh/mh
+
+ registry.add_field((ftype,"mazzotta_weighting"),
+ function=_mazzotta_weighting,
+ units="keV**-0.25*cm**-6")
+
def _sz_kinetic(field, data):
scale = 0.88 * sigma_thompson / mh / clight
vel_axis = data.get_field_parameter("axis")
diff -r 1b18659fba23a51604134b163716a7cbb6f609e5 -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 yt/units/dimensions.py
--- a/yt/units/dimensions.py
+++ b/yt/units/dimensions.py
@@ -19,9 +19,11 @@
time = Symbol("(time)", positive=True)
temperature = Symbol("(temperature)", positive=True)
angle = Symbol("(angle)", positive=True)
+current_mks = Symbol("(current_mks)", positive=True)
dimensionless = sympify(1)
-base_dimensions = [mass, length, time, temperature, angle, dimensionless]
+base_dimensions = [mass, length, time, temperature, angle, current_mks,
+ dimensionless]
#
# Derived dimensions
@@ -44,16 +46,31 @@
power = energy / time
flux = power / area
specific_flux = flux / rate
-charge = (energy * length)**Rational(1, 2) # proper 1/2 power
+number_density = 1/(length*length*length)
+density = mass * number_density
-electric_field = charge / length**2
-magnetic_field = electric_field
+# Gaussian electromagnetic units
+charge_cgs = (energy * length)**Rational(1, 2) # proper 1/2 power
+current_cgs = charge_cgs / time
+electric_field_cgs = charge_cgs / length**2
+magnetic_field_cgs = electric_field_cgs
+
+# SI electromagnetic units
+charge_mks = current_mks * time
+electric_field_mks = force / charge_mks
+magnetic_field_mks = electric_field_mks / velocity
+
+# Since cgs is our default, I'm adding these aliases for backwards-compatibility
+charge = charge_cgs
+electric_field = electric_field_cgs
+magnetic_field = magnetic_field_cgs
solid_angle = angle * angle
derived_dimensions = [rate, velocity, acceleration, jerk, snap, crackle, pop,
- momentum, force, energy, power, charge, electric_field,
- magnetic_field, solid_angle, flux, specific_flux, volume,
- area]
+ momentum, force, energy, power, charge_cgs, electric_field_cgs,
+ magnetic_field_cgs, solid_angle, flux, specific_flux, volume,
+ area, current_cgs, charge_mks, electric_field_mks,
+ magnetic_field_mks]
dimensions = base_dimensions + derived_dimensions
diff -r 1b18659fba23a51604134b163716a7cbb6f609e5 -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 yt/units/equivalencies.py
--- /dev/null
+++ b/yt/units/equivalencies.py
@@ -0,0 +1,166 @@
+"""
+Equivalencies between different kinds of units
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2013, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+import yt.utilities.physical_constants as pc
+from yt.units.dimensions import temperature, mass, energy, length, rate, \
+ velocity, dimensionless, density, number_density, flux
+from yt.extern.six import add_metaclass
+import numpy as np
+
+equivalence_registry = {}
+
+class RegisteredEquivalence(type):
+ def __init__(cls, name, b, d):
+ type.__init__(cls, name, b, d)
+ if hasattr(cls, "_type_name") and not cls._skip_add:
+ equivalence_registry[cls._type_name] = cls
+
+ at add_metaclass(RegisteredEquivalence)
+class Equivalence(object):
+ _skip_add = False
+
+class NumberDensityEquivalence(Equivalence):
+ _type_name = "number_density"
+ dims = (density,number_density,)
+
+ def convert(self, x, new_dims, mu=0.6):
+ if new_dims == number_density:
+ return x/(mu*pc.mh)
+ elif new_dims == density:
+ return x*mu*pc.mh
+
+ def __str__(self):
+ return "number density: density <-> number density"
+
+class ThermalEquivalence(Equivalence):
+ _type_name = "thermal"
+ dims = (temperature,energy,)
+
+ def convert(self, x, new_dims):
+ if new_dims == energy:
+ return pc.kboltz*x
+ elif new_dims == temperature:
+ return x/pc.kboltz
+
+ def __str__(self):
+ return "thermal: temperature <-> energy"
+
+class MassEnergyEquivalence(Equivalence):
+ _type_name = "mass_energy"
+ dims = (mass,energy,)
+
+ def convert(self, x, new_dims):
+ if new_dims == energy:
+ return x*pc.clight*pc.clight
+ elif new_dims == mass:
+ return x/(pc.clight*pc.clight)
+
+ def __str__(self):
+ return "mass_energy: mass <-> energy"
+
+class SpectralEquivalence(Equivalence):
+ _type_name = "spectral"
+ dims = (length,rate,energy,)
+
+ def convert(self, x, new_dims):
+ if new_dims == energy:
+ if x.units.dimensions == length:
+ nu = pc.clight/x
+ elif x.units.dimensions == rate:
+ nu = x
+ return pc.hcgs*nu
+ elif new_dims == length:
+ if x.units.dimensions == rate:
+ return pc.clight/x
+ elif x.units.dimensions == energy:
+ return pc.hcgs*pc.clight/x
+ elif new_dims == rate:
+ if x.units.dimensions == length:
+ return pc.clight/x
+ elif x.units.dimensions == energy:
+ return x/pc.hcgs
+
+ def __str__(self):
+ return "spectral: length <-> rate <-> energy"
+
+class SoundSpeedEquivalence(Equivalence):
+ _type_name = "sound_speed"
+ dims = (velocity,temperature,energy,)
+
+ def convert(self, x, new_dims, mu=0.6, gamma=5./3.):
+ if new_dims == velocity:
+ if x.units.dimensions == temperature:
+ kT = pc.kboltz*x
+ elif x.units.dimensions == energy:
+ kT = x
+ return np.sqrt(gamma*kT/(mu*pc.mh))
+ else:
+ kT = x*x*mu*pc.mh/gamma
+ if new_dims == temperature:
+ return kT/pc.kboltz
+ else:
+ return kT
+
+ def __str__(self):
+ return "sound_speed (ideal gas): velocity <-> temperature <-> energy"
+
+class LorentzEquivalence(Equivalence):
+ _type_name = "lorentz"
+ dims = (dimensionless,velocity,)
+
+ def convert(self, x, new_dims):
+ if new_dims == dimensionless:
+ beta = x.in_cgs()/pc.clight
+ return 1./np.sqrt(1.-beta**2)
+ elif new_dims == velocity:
+ return pc.clight*np.sqrt(1.-1./(x*x))
+
+ def __str__(self):
+ return "lorentz: velocity <-> dimensionless"
+
+class SchwarzschildEquivalence(Equivalence):
+ _type_name = "schwarzschild"
+ dims = (mass,length,)
+
+ def convert(self, x, new_dims):
+ if new_dims == length:
+ return 2.*pc.G*x/(pc.clight*pc.clight)
+ elif new_dims == mass:
+ return 0.5*x*pc.clight*pc.clight/pc.G
+
+ def __str__(self):
+ return "schwarzschild: mass <-> length"
+
+class ComptonEquivalence(Equivalence):
+ _type_name = "compton"
+ dims = (mass,length,)
+
+ def convert(self, x, new_dims):
+ return pc.hcgs/(x*pc.clight)
+
+ def __str__(self):
+ return "compton: mass <-> length"
+
+class EffectiveTemperature(Equivalence):
+ _type_name = "effective_temperature"
+ dims = (flux,temperature,)
+
+ def convert(self, x, new_dims):
+ if new_dims == flux:
+ return pc.stefan_boltzmann_constant_cgs*x**4
+ elif new_dims == temperature:
+ return (x/pc.stefan_boltzmann_constant_cgs)**0.25
+
+ def __str__(self):
+ return "effective_temperature: flux <-> temperature"
+
diff -r 1b18659fba23a51604134b163716a7cbb6f609e5 -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 yt/units/tests/test_ytarray.py
--- a/yt/units/tests/test_ytarray.py
+++ b/yt/units/tests/test_ytarray.py
@@ -28,7 +28,8 @@
assert_array_equal, \
assert_equal, assert_raises, \
assert_array_almost_equal_nulp, \
- assert_array_almost_equal
+ assert_array_almost_equal, \
+ assert_allclose
from numpy import array
from yt.units.yt_array import \
YTArray, YTQuantity, \
@@ -471,21 +472,21 @@
km = YTQuantity(1, 'km')
balmy = YTQuantity(300, 'K')
- balmy_F = YTQuantity(80.33, 'F')
- balmy_C = YTQuantity(26.85, 'C')
+ balmy_F = YTQuantity(80.33, 'degF')
+ balmy_C = YTQuantity(26.85, 'degC')
balmy_R = YTQuantity(540, 'R')
- assert_array_almost_equal(balmy.in_units('F'), balmy_F)
- assert_array_almost_equal(balmy.in_units('C'), balmy_C)
+ assert_array_almost_equal(balmy.in_units('degF'), balmy_F)
+ assert_array_almost_equal(balmy.in_units('degC'), balmy_C)
assert_array_almost_equal(balmy.in_units('R'), balmy_R)
balmy_view = balmy.ndarray_view()
- balmy.convert_to_units('F')
+ balmy.convert_to_units('degF')
yield assert_true, balmy_view.base is balmy.base
yield assert_array_almost_equal, np.array(balmy), np.array(balmy_F)
- balmy.convert_to_units('C')
+ balmy.convert_to_units('degC')
yield assert_true, balmy_view.base is balmy.base
yield assert_array_almost_equal, np.array(balmy), np.array(balmy_C)
@@ -493,13 +494,13 @@
yield assert_true, balmy_view.base is balmy.base
yield assert_array_almost_equal, np.array(balmy), np.array(balmy_R)
- balmy.convert_to_units('F')
+ balmy.convert_to_units('degF')
yield assert_true, balmy_view.base is balmy.base
yield assert_array_almost_equal, np.array(balmy), np.array(balmy_F)
yield assert_raises, InvalidUnitOperation, np.multiply, balmy, km
- # Does CGS convergion from F to K work?
+ # Does CGS conversion from F to K work?
yield assert_array_almost_equal, balmy.in_cgs(), YTQuantity(300, 'K')
@@ -859,6 +860,114 @@
os.chdir(curdir)
shutil.rmtree(tmpdir)
+def test_cgs_conversions():
+ from yt.utilities.physical_constants import qp, eps_0, clight
+ from yt.units.dimensions import current_mks, time
+
+ qp_mks = qp.in_units("C")
+ yield assert_equal, qp_mks.units.dimensions, current_mks*time
+ yield assert_array_almost_equal, qp_mks.in_cgs(), qp
+ qp_mks_k = qp_mks.in_units("kC")
+ yield assert_equal, qp_mks.units.dimensions, current_mks*time
+ yield assert_array_almost_equal, qp_mks_k.in_cgs(), qp
+ yield assert_array_almost_equal, qp_mks_k.in_units("kesu"), qp.in_units("kesu")
+
+ K = 1.0/(4*np.pi*eps_0)
+ yield assert_array_almost_equal, K.in_cgs(), 1.0
+
+ B = YTQuantity(1.0, "T")
+ yield assert_array_almost_equal, B.in_units("gauss"), YTQuantity(1.0e4, "gauss")
+
+ I = YTQuantity(1.0, "A")
+ yield assert_array_almost_equal, I.in_units("statA"), YTQuantity(0.1*clight, "statA")
+
+def test_equivalencies():
+ from yt.utilities.physical_constants import clight, mp, kboltz, hcgs, mh, me, \
+ mass_sun_cgs, G, stefan_boltzmann_constant_cgs
+ import yt.units as u
+
+ # Mass-energy
+
+ E = mp.to_equivalent("keV","mass_energy")
+ yield assert_equal, E, mp*clight*clight
+ yield assert_allclose, mp, E.to_equivalent("g", "mass_energy")
+
+ # Thermal
+
+ T = YTQuantity(1.0e8,"K")
+ E = T.to_equivalent("W*hr","thermal")
+ yield assert_equal, E, (kboltz*T).in_units("W*hr")
+ yield assert_allclose, T, E.to_equivalent("K", "thermal")
+
+ # Spectral
+
+ l = YTQuantity(4000.,"angstrom")
+ nu = l.to_equivalent("Hz","spectral")
+ yield assert_equal, nu, clight/l
+ E = hcgs*nu
+ l2 = E.to_equivalent("angstrom", "spectral")
+ yield assert_allclose, l, l2
+ nu2 = clight/l2.in_units("cm")
+ yield assert_allclose, nu, nu2
+ E2 = nu2.to_equivalent("keV", "spectral")
+ yield assert_allclose, E2, E.in_units("keV")
+
+ # Sound-speed
+
+ mu = 0.6
+ gg = 5./3.
+ c_s = T.to_equivalent("km/s","sound_speed")
+ yield assert_equal, c_s, np.sqrt(gg*kboltz*T/(mu*mh))
+ yield assert_allclose, T, c_s.to_equivalent("K","sound_speed")
+
+ mu = 0.5
+ gg = 4./3.
+ c_s = T.to_equivalent("km/s","sound_speed", mu=mu, gamma=gg)
+ yield assert_equal, c_s, np.sqrt(gg*kboltz*T/(mu*mh))
+ yield assert_allclose, T, c_s.to_equivalent("K","sound_speed",
+ mu=mu, gamma=gg)
+
+ # Lorentz
+
+ v = 0.8*clight
+ g = v.to_equivalent("dimensionless","lorentz")
+ g2 = YTQuantity(1./np.sqrt(1.-0.8*0.8), "dimensionless")
+ yield assert_allclose, g, g2
+ v2 = g2.to_equivalent("mile/hr", "lorentz")
+ yield assert_allclose, v2, v.in_units("mile/hr")
+
+ # Schwarzschild
+
+ R = mass_sun_cgs.to_equivalent("kpc","schwarzschild")
+ yield assert_equal, R.in_cgs(), 2*G*mass_sun_cgs/(clight*clight)
+ yield assert_allclose, mass_sun_cgs, R.to_equivalent("g", "schwarzschild")
+
+ # Compton
+
+ l = me.to_equivalent("angstrom","compton")
+ yield assert_equal, l, hcgs/(me*clight)
+ yield assert_allclose, me, l.to_equivalent("g", "compton")
+
+ # Number density
+
+ rho = mp/u.cm**3
+
+ n = rho.to_equivalent("cm**-3","number_density")
+ yield assert_equal, n, rho/(mh*0.6)
+ yield assert_allclose, rho, n.to_equivalent("g/cm**3","number_density")
+
+ n = rho.to_equivalent("cm**-3","number_density", mu=0.75)
+ yield assert_equal, n, rho/(mh*0.75)
+ yield assert_allclose, rho, n.to_equivalent("g/cm**3","number_density", mu=0.75)
+
+ # Effective temperature
+
+ T = YTQuantity(1.0e4, "K")
+ F = T.to_equivalent("erg/s/cm**2","effective_temperature")
+ yield assert_equal, F, stefan_boltzmann_constant_cgs*T**4
+ yield assert_allclose, T, F.to_equivalent("K", "effective_temperature")
+
+
def test_numpy_wrappers():
a1 = YTArray([1, 2, 3], 'cm')
a2 = YTArray([2, 3, 4, 5, 6], 'cm')
diff -r 1b18659fba23a51604134b163716a7cbb6f609e5 -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 yt/units/unit_lookup_table.py
--- a/yt/units/unit_lookup_table.py
+++ b/yt/units/unit_lookup_table.py
@@ -20,7 +20,8 @@
metallicity_sun, erg_per_eV, amu_grams, mass_electron_grams, \
cm_per_ang, jansky_cgs, mass_jupiter_grams, mass_earth_grams, \
boltzmann_constant_erg_per_K, kelvin_per_rankine, \
- speed_of_light_cm_per_s
+ speed_of_light_cm_per_s, planck_length_cm, planck_charge_esu, \
+ planck_energy_erg, planck_mass_grams, planck_temperature_K, planck_time_s
import numpy as np
# Lookup a unit symbol with the symbol string, and provide a tuple with the
@@ -37,20 +38,25 @@
# other cgs
"dyne": (1.0, dimensions.force),
"erg": (1.0, dimensions.energy),
- "esu": (1.0, dimensions.charge),
- "gauss": (1.0, dimensions.magnetic_field),
- "C" : (1.0, dimensions.temperature, -273.15),
+ "esu": (1.0, dimensions.charge_cgs),
+ "gauss": (1.0, dimensions.magnetic_field_cgs),
+ "degC": (1.0, dimensions.temperature, -273.15),
+ "statA": (1.0, dimensions.current_cgs),
# some SI
"m": (1.0e2, dimensions.length),
"J": (1.0e7, dimensions.energy),
"W": (1.0e7, dimensions.power),
"Hz": (1.0, dimensions.rate),
+ "N": (1.0e5, dimensions.force),
+ "C": (0.1*speed_of_light_cm_per_s, dimensions.charge_mks),
+ "A": (0.1*speed_of_light_cm_per_s, dimensions.current_mks),
+ "T": (1.0e4, dimensions.magnetic_field_mks),
# Imperial units
"ft": (30.48, dimensions.length),
"mile": (160934, dimensions.length),
- "F": (kelvin_per_rankine, dimensions.temperature, -459.67),
+ "degF": (kelvin_per_rankine, dimensions.temperature, -459.67),
"R": (kelvin_per_rankine, dimensions.temperature),
# dimensionless stuff
@@ -93,13 +99,11 @@
# misc
"eV": (erg_per_eV, dimensions.energy),
"amu": (amu_grams, dimensions.mass),
- "me": (mass_electron_grams, dimensions.mass),
"angstrom": (cm_per_ang, dimensions.length),
"Jy": (jansky_cgs, dimensions.specific_flux),
"counts": (1.0, dimensions.dimensionless),
- "kB": (boltzmann_constant_erg_per_K,
- dimensions.energy/dimensions.temperature),
"photons": (1.0, dimensions.dimensionless),
+ "me": (mass_electron_grams, dimensions.mass),
# for AstroPy compatibility
"solMass": (mass_sun_grams, dimensions.mass),
@@ -109,11 +113,19 @@
"sr": (1.0, dimensions.solid_angle),
"rad": (1.0, dimensions.solid_angle),
"deg": (np.pi/180., dimensions.angle),
- "Fr": (1.0, dimensions.charge),
- "G": (1.0, dimensions.magnetic_field),
+ "Fr": (1.0, dimensions.charge_cgs),
+ "G": (1.0, dimensions.magnetic_field_cgs),
"d": (1.0, dimensions.time),
"Angstrom": (cm_per_ang, dimensions.length),
+ # Planck units
+ "m_pl": (planck_mass_grams, dimensions.mass),
+ "l_pl": (planck_length_cm, dimensions.length),
+ "t_pl": (planck_time_s, dimensions.time),
+ "T_pl": (planck_temperature_K, dimensions.temperature),
+ "q_pl": (planck_charge_esu, dimensions.charge_cgs),
+ "E_pl": (planck_energy_erg, dimensions.energy),
+
}
# Add LaTeX representations for units with trivial representations.
@@ -183,6 +195,10 @@
"gauss",
"G",
"Jy",
+ "N",
+ "T",
+ "A",
+ "C",
)
cgs_base_units = {
@@ -199,4 +215,16 @@
dimensions.time:'s',
dimensions.temperature:'K',
dimensions.angle:'radian',
+ dimensions.current_mks:'A',
}
+
+cgs_conversions = {
+ "C":"esu",
+ "T":"gauss",
+ "A":"statA",
+}
+
+for conv in cgs_conversions.keys():
+ if conv in prefixable_units:
+ for p in unit_prefixes:
+ cgs_conversions[p+conv] = p+cgs_conversions[conv]
diff -r 1b18659fba23a51604134b163716a7cbb6f609e5 -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 yt/units/unit_object.py
--- a/yt/units/unit_object.py
+++ b/yt/units/unit_object.py
@@ -17,7 +17,7 @@
Pow, Symbol, Integer, \
Float, Basic, Rational, sqrt
from sympy.core.numbers import One
-from sympy import sympify, latex
+from sympy import sympify, latex, symbols
from sympy.parsing.sympy_parser import \
parse_expr, auto_number, rationalize
from keyword import iskeyword
@@ -26,7 +26,8 @@
from yt.units.unit_lookup_table import \
latex_symbol_lut, unit_prefixes, \
prefixable_units, cgs_base_units, \
- mks_base_units, latex_prefixes
+ mks_base_units, latex_prefixes, \
+ cgs_conversions
from yt.units.unit_registry import UnitRegistry
import copy
@@ -116,7 +117,7 @@
# Extra attributes
__slots__ = ["expr", "is_atomic", "cgs_value", "cgs_offset", "dimensions",
- "registry"]
+ "registry", "cgs_conversion", "is_mks"]
def __new__(cls, unit_expr=sympy_one, cgs_value=None, cgs_offset=0.0,
dimensions=None, registry=None, **assumptions):
@@ -191,12 +192,11 @@
validate_dimensions(dimensions)
else:
# lookup the unit symbols
- try:
- cgs_value, dimensions = \
- _get_unit_data_from_expr(unit_expr, registry.lut)
- except ValueError:
- cgs_value, dimensions, cgs_offset = \
- _get_unit_data_from_expr(unit_expr, registry.lut)
+ unit_data = _get_unit_data_from_expr(unit_expr, registry.lut)
+ cgs_value = unit_data[0]
+ dimensions = unit_data[1]
+ if len(unit_data) == 3:
+ cgs_offset = unit_data[2]
# Create obj with superclass construct.
obj = Expr.__new__(cls, **assumptions)
@@ -209,6 +209,22 @@
obj.dimensions = dimensions
obj.registry = registry
+ check_atoms = [atom for atom in unit_expr.free_symbols
+ if str(atom) in cgs_conversions]
+ if len(check_atoms) > 0:
+ conversions = []
+ for atom in check_atoms:
+ conversions.append((atom,symbols(cgs_conversions[str(atom)])))
+ conversion = unit_expr.subs(conversions)
+ conversion = Unit(unit_expr=conversion, cgs_value=1.0,
+ dimensions=None, registry=registry)
+ is_mks = True
+ else:
+ conversion = None
+ is_mks = False
+ obj.cgs_conversion = conversion
+ obj.is_mks = is_mks
+
if unit_key:
registry.unit_objs[unit_key] = obj
@@ -264,7 +280,7 @@
cgs_offset = self.cgs_offset
else:
raise InvalidUnitOperation("Quantities with units of Fahrenheit "
- "and Celcius cannot be multiplied.")
+ "and Celsius cannot be multiplied.")
return Unit(self.expr * u.expr,
cgs_value=(self.cgs_value * u.cgs_value),
@@ -287,7 +303,7 @@
cgs_offset = self.cgs_offset
else:
raise InvalidUnitOperation("Quantities with units of Farhenheit "
- "and Celcius cannot be multiplied.")
+ "and Celsius cannot be multiplied.")
return Unit(self.expr / u.expr,
cgs_value=(self.cgs_value / u.cgs_value),
@@ -307,7 +323,8 @@
"it to a float." % (p, type(p)) )
return Unit(self.expr**p, cgs_value=(self.cgs_value**p),
- dimensions=(self.dimensions**p), registry=self.registry)
+ dimensions=(self.dimensions**p),
+ registry=self.registry)
def __eq__(self, u):
""" Test unit equality. """
@@ -340,6 +357,14 @@
def same_dimensions_as(self, other_unit):
""" Test if dimensions are the same. """
+ first_check = False
+ second_check = False
+ if self.cgs_conversion:
+ first_check = self.cgs_conversion.dimensions / other_unit.dimensions == sympy_one
+ if other_unit.cgs_conversion:
+ second_check = other_unit.cgs_conversion.dimensions / self.dimensions == sympy_one
+ if first_check or second_check:
+ return True
return (self.dimensions / other_unit.dimensions) == sympy_one
@property
@@ -372,20 +397,28 @@
Create and return dimensionally-equivalent cgs units.
"""
- units_string = self._get_system_unit_string(cgs_base_units)
+ if self.cgs_conversion:
+ units = self.cgs_conversion
+ else:
+ units = self
+ units_string = units._get_system_unit_string(cgs_base_units)
return Unit(units_string, cgs_value=1.0,
- dimensions=self.dimensions, registry=self.registry)
+ dimensions=units.dimensions, registry=self.registry)
def get_mks_equivalent(self):
"""
Create and return dimensionally-equivalent mks units.
"""
- units_string = self._get_system_unit_string(mks_base_units)
- cgs_value = (get_conversion_factor(self, self.get_cgs_equivalent())[0] /
- get_conversion_factor(self, Unit(units_string))[0])
+ if self.cgs_conversion and not self.is_mks:
+ units = self.cgs_conversion
+ else:
+ units = self
+ units_string = units._get_system_unit_string(mks_base_units)
+ cgs_value = (get_conversion_factor(units, units.get_cgs_equivalent())[0] /
+ get_conversion_factor(units, Unit(units_string))[0])
return Unit(units_string, cgs_value=cgs_value,
- dimensions=self.dimensions, registry=self.registry)
+ dimensions=units.dimensions, registry=self.registry)
def get_conversion_factor(self, other_units):
return get_conversion_factor(self, other_units)
@@ -430,7 +463,7 @@
return ratio, ratio*old_units.cgs_offset - new_units.cgs_offset
else:
raise InvalidUnitOperation(
- "Fahrenheit and Celsius are not absoulte temperature scales "
+ "Fahrenheit and Celsius are not absolute temperature scales "
"and cannot be used in compound unit symbols.")
#
diff -r 1b18659fba23a51604134b163716a7cbb6f609e5 -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 yt/units/unit_symbols.py
--- a/yt/units/unit_symbols.py
+++ b/yt/units/unit_symbols.py
@@ -140,8 +140,8 @@
MeV = mega_electron_volt = quan(1.0, "MeV")
GeV = giga_electron_volt = quan(1.0, "GeV")
amu = atomic_mass_unit = quan(1.0, "amu")
+angstrom = quan(1.0, "angstrom")
me = electron_mass = quan(1.0, "me")
-angstrom = quan(1.0, "angstrom")
#
# Angle units
diff -r 1b18659fba23a51604134b163716a7cbb6f609e5 -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 yt/units/yt_array.py
--- a/yt/units/yt_array.py
+++ b/yt/units/yt_array.py
@@ -33,10 +33,12 @@
from yt.units.dimensions import dimensionless
from yt.utilities.exceptions import \
YTUnitOperationError, YTUnitConversionError, \
- YTUfuncUnitError, YTIterableUnitCoercionError
+ YTUfuncUnitError, YTIterableUnitCoercionError, \
+ YTInvalidUnitEquivalence
from numbers import Number as numeric_type
from yt.utilities.on_demand_imports import _astropy
from sympy import Rational
+from yt.units.unit_lookup_table import unit_prefixes, prefixable_units
NULL_UNIT = Unit()
@@ -458,6 +460,44 @@
"""
return self.in_units(self.units.get_mks_equivalent())
+ def to_equivalent(self, unit, equiv, **kwargs):
+ """
+ Convert a YTArray or YTQuantity to an equivalent, e.g., something that is
+ related by only a constant factor but not in the same units.
+
+ Parameters
+ ----------
+ unit : string
+ The unit that you wish to convert to.
+ equiv : string
+ The equivalence you wish to use. To see which equivalencies are
+ supported for this unitful quantity, try the :method:`list_equivalencies`
+ method.
+
+ Examples
+ --------
+ >>> a = yt.YTArray(1.0e7,"K")
+ >>> a.to_equivalent("keV", "thermal")
+ """
+ from equivalencies import equivalence_registry
+ this_equiv = equivalence_registry[equiv]()
+ old_dims = self.units.dimensions
+ new_dims = YTQuantity(1.0, unit, registry=self.units.registry).units.dimensions
+ if old_dims in this_equiv.dims and new_dims in this_equiv.dims:
+ return this_equiv.convert(self, new_dims, **kwargs).in_units(unit)
+ else:
+ raise YTInvalidUnitEquivalence(equiv, self.units, unit)
+
+ def list_equivalencies(self):
+ """
+ Lists the possible equivalencies associated with this YTArray or
+ YTQuantity.
+ """
+ from equivalencies import equivalence_registry
+ for k,v in equivalence_registry.items():
+ if self.units.dimensions in v.dims:
+ print v()
+
def ndarray_view(self):
"""
Returns a view into the array, but as an ndarray rather than ytarray.
diff -r 1b18659fba23a51604134b163716a7cbb6f609e5 -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 yt/utilities/exceptions.py
--- a/yt/utilities/exceptions.py
+++ b/yt/utilities/exceptions.py
@@ -426,7 +426,6 @@
def __str__(self):
return "A file already exists at %s and clobber=False." % self.filename
-
class YTGDFUnknownGeometry(Exception):
def __init__(self, geometry):
self.geometry = geometry
@@ -435,4 +434,14 @@
return '''Unknown geometry %i. Please refer to GDF standard
for more information''' % self.geometry
+class YTInvalidUnitEquivalence(Exception):
+ def __init__(self, equiv, unit1, unit2):
+ self.equiv = equiv
+ self.unit1 = unit1
+ self.unit2 = unit2
+ def __str__(self):
+ return "The unit equivalence '%s' does not exist for the units '%s' and '%s.'" % (self.equiv,
+ self.unit1,
+ self.unit2)
+
diff -r 1b18659fba23a51604134b163716a7cbb6f609e5 -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 yt/utilities/physical_constants.py
--- a/yt/utilities/physical_constants.py
+++ b/yt/utilities/physical_constants.py
@@ -1,5 +1,6 @@
from yt.utilities.physical_ratios import *
from yt.units.yt_array import YTQuantity
+from math import pi
mass_electron_cgs = YTQuantity(mass_electron_grams, 'g')
amu_cgs = YTQuantity(amu_grams, 'g')
@@ -14,6 +15,7 @@
# Charge
charge_proton_cgs = YTQuantity(4.8032056e-10, 'esu')
+elementary_charge = charge_proton_cgs
# Physical Constants
boltzmann_constant_cgs = YTQuantity(boltzmann_constant_erg_per_K, 'erg/K')
@@ -35,7 +37,7 @@
mass_mars_cgs = YTQuantity(mass_mars_grams, 'g')
mass_saturn_cgs = YTQuantity(mass_saturn_grams, 'g')
mass_uranus_cgs = YTQuantity(mass_uranus_grams, 'g')
-mass_neptun_cgs = YTQuantity(mass_neptun_grams, 'g')
+mass_neptune_cgs = YTQuantity(mass_neptune_grams, 'g')
#Short cuts
G = gravitational_constant_cgs
@@ -48,5 +50,17 @@
kboltz = boltzmann_constant_cgs
kb = kboltz
hcgs = planck_constant_cgs
+hbar = 0.5*hcgs/pi
sigma_thompson = cross_section_thompson_cgs
Na = 1 / amu_cgs
+
+#Planck units
+m_pl = planck_mass = YTQuantity(planck_mass_grams, "g")
+l_pl = planck_length = YTQuantity(planck_length_cm, "cm")
+t_pl = planck_time = YTQuantity(planck_time_s, "s")
+E_pl = planck_energy = YTQuantity(planck_energy_erg, "erg")
+q_pl = planck_charge = YTQuantity(planck_charge_esu, "esu")
+T_pl = planck_temperature = YTQuantity(planck_temperature_K, "K")
+
+mu_0 = YTQuantity(4.0e-7*pi, "N/A**2")
+eps_0 = (1.0/(clight.in_mks()**2*mu_0)).in_units("C**2/N/m**2")
diff -r 1b18659fba23a51604134b163716a7cbb6f609e5 -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 yt/utilities/physical_ratios.py
--- a/yt/utilities/physical_ratios.py
+++ b/yt/utilities/physical_ratios.py
@@ -92,7 +92,7 @@
mass_mars_grams = mass_sun_grams / 3098708.0
mass_saturn_grams = mass_sun_grams / 3497.898
mass_uranus_grams = mass_sun_grams / 22902.98
-mass_neptun_grams = mass_sun_grams / 19412.24
+mass_neptune_grams = mass_sun_grams / 19412.24
# flux
jansky_cgs = 1.0e-23
@@ -109,3 +109,11 @@
# Miscellaneous
HUGE = 1.0e90
TINY = 1.0e-40
+
+# Planck units
+planck_mass_grams = 2.17650925245e-05
+planck_length_cm = 1.6161992557e-33
+planck_time_s = planck_length_cm / speed_of_light_cm_per_s
+planck_energy_erg = planck_mass_grams * speed_of_light_cm_per_s * speed_of_light_cm_per_s
+planck_temperature_K = planck_energy_erg / boltzmann_constant_erg_per_K
+planck_charge_esu = 5.62274532302e-09
https://bitbucket.org/yt_analysis/yt/commits/2030b40ee37e/
Changeset: 2030b40ee37e
Branch: yt
User: jzuhone
Date: 2014-11-03 22:12:22+00:00
Summary: Updating docstrings everywhere to reflect the new options. Updating center and width implementations for SZ projections and PPVCube.
Affected #: 6 files
diff -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 -r 2030b40ee37eb48bc0383a6e0cc844a97b5e0eab yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -18,6 +18,7 @@
from yt.funcs import get_pbar
from yt.utilities.physical_constants import clight, mh
import yt.units.dimensions as ytdims
+from yt.units.yt_array import YTQuantity
from yt.funcs import iterable
from yt.utilities.parallel_tools.parallel_analysis_interface import \
parallel_root_only, parallel_objects
@@ -60,11 +61,20 @@
principal axes of the domain ("x","y", or "z").
field : string
The field to project.
- center : float, tuple, or string
- The coordinates of the dataset *ds* on which to center the PPVCube.
- width : float or tuple, optional
- The width of the projection in length units. Specify a float
- for code_length units or a tuple (value, units).
+ center : A sequence of floats, a string, or a tuple.
+ The coordinate of the center of the image. If set to 'c', 'center' or
+ left blank, the plot is centered on the middle of the domain. If set to
+ 'max' or 'm', the center will be located at the maximum of the
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
+ as a tuple containing a coordinate and string unit name or by passing
+ in a YTArray. If a list or unitless array is supplied, code units are
+ assumed.
+ width : float, tuple, or YTQuantity.
+ The width of the projection. A float will assume the width is in code units.
+ A (value, unit) tuple or YTQuantity allows for the units of the width to be
+ specified.
dims : tuple, optional
A 3-tuple of dimensions (nx,ny,nv) for the cube.
velocity_bounds : tuple, optional
@@ -163,6 +173,8 @@
# Now fix the width
if iterable(self.width):
self.width = ds.quan(self.width[0], self.width[1])
+ elif isinstance(self.width, YTQuantity):
+ self.width = width
else:
self.width = ds.quan(self.width, "code_length")
diff -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 -r 2030b40ee37eb48bc0383a6e0cc844a97b5e0eab yt/analysis_modules/sunyaev_zeldovich/projection.py
--- a/yt/analysis_modules/sunyaev_zeldovich/projection.py
+++ b/yt/analysis_modules/sunyaev_zeldovich/projection.py
@@ -115,10 +115,19 @@
----------
axis : integer or string
The axis of the simulation domain along which to make the SZprojection.
- center : array_like or string, optional
- The center of the projection.
- width : float or tuple
- The width of the projection.
+ center : A sequence of floats, a string, or a tuple.
+ The coordinate of the center of the image. If set to 'c', 'center' or
+ left blank, the plot is centered on the middle of the domain. If set to
+ 'max' or 'm', the center will be located at the maximum of the
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
+ as a tuple containing a coordinate and string unit name or by passing
+ in a YTArray. If a list or unitless array is supplied, code units are
+ assumed.
+ width : float, tuple, or YTQuantity.
+ The width of the projection. A float will assume the width is in code units.
+ A (value, unit) tuple or YTQuantity allows for the units of the width to be specified.
nx : integer, optional
The dimensions on a side of the projection image.
source : yt.data_objects.data_containers.YTSelectionContainer, optional
@@ -129,13 +138,8 @@
>>> szprj.on_axis("y", center="max", width=(1.0, "Mpc"), source=my_sphere)
"""
axis = fix_axis(axis, self.ds)
-
- if center == "c":
- ctr = self.ds.domain_center
- elif center == "max":
- v, ctr = self.ds.find_max("density")
- else:
- ctr = center
+ ctr, dctr = self.ds.coordinates.sanitize_center(center, axis)
+ width = self.ds.coordinates.sanitize_width(axis, width, None)
L = np.zeros((3))
L[axis] = 1.0
@@ -177,10 +181,19 @@
----------
L : array_like
The normal vector of the projection.
- center : array_like or string, optional
- The center of the projection.
- width : float or tuple
- The width of the projection.
+ center : A sequence of floats, a string, or a tuple.
+ The coordinate of the center of the image. If set to 'c', 'center' or
+ left blank, the plot is centered on the middle of the domain. If set to
+ 'max' or 'm', the center will be located at the maximum of the
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
+ as a tuple containing a coordinate and string unit name or by passing
+ in a YTArray. If a list or unitless array is supplied, code units are
+ assumed.
+ width : float, tuple, or YTQuantity.
+ The width of the projection. A float will assume the width is in code units.
+ A (value, unit) tuple or YTQuantity allows for the units of the width to be specified.
nx : integer, optional
The dimensions on a side of the projection image.
source : yt.data_objects.data_containers.YTSelectionContainer, optional
@@ -198,12 +211,7 @@
w = width.in_units("code_length").value
else:
w = width
- if center == "c":
- ctr = self.ds.domain_center
- elif center == "max":
- v, ctr = self.ds.find_max("density")
- else:
- ctr = center
+ ctr, dctr = self.ds.coordinates.sanitize_center(center, L)
if source is not None:
mylog.error("Source argument is not currently supported for off-axis S-Z projections.")
diff -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 -r 2030b40ee37eb48bc0383a6e0cc844a97b5e0eab yt/data_objects/construction_data_containers.py
--- a/yt/data_objects/construction_data_containers.py
+++ b/yt/data_objects/construction_data_containers.py
@@ -191,6 +191,8 @@
"integrate" : integration along the axis
"mip" : maximum intensity projection
"sum" : same as "integrate", except that we don't multiply by the path length
+ WARNING: The "sum" option should only be used for uniform resolution grid
+ datasets, as other datasets may result in unphysical images.
style : string, optional
The same as the method keyword. Deprecated as of version 3.0.2.
Please use method keyword instead.
diff -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 -r 2030b40ee37eb48bc0383a6e0cc844a97b5e0eab yt/utilities/fits_image.py
--- a/yt/utilities/fits_image.py
+++ b/yt/utilities/fits_image.py
@@ -321,13 +321,15 @@
The axis of the slice. One of "x","y","z", or 0,1,2.
fields : string or list of strings
The fields to slice
- center : A sequence floats, a string, or a tuple.
- The coordinate of the origin of the image. If set to 'c', 'center' or
+ center : A sequence of floats, a string, or a tuple.
+ The coordinate of the center of the image. If set to 'c', 'center' or
left blank, the plot is centered on the middle of the domain. If set to
'max' or 'm', the center will be located at the maximum of the
- ('gas', 'density') field. Units can be specified by passing in center
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
as a tuple containing a coordinate and string unit name or by passing
- in a YTArray. If a list or unitless array is supplied, code units are
+ in a YTArray. If a list or unitless array is supplied, code units are
assumed.
width : tuple or a float.
Width can have four different formats to support windows with variable
@@ -378,14 +380,16 @@
The fields to project
weight_field : string
The field used to weight the projection.
- center : A sequence floats, a string, or a tuple.
- The coordinate of the origin of the image. If set to 'c', 'center' or
- left blank, the plot is centered on the middle of the domain. If set to
- 'max' or 'm', the center will be located at the maximum of the
- ('gas', 'density') field. Units can be specified by passing in center
- as a tuple containing a coordinate and string unit name or by passing
- in a YTArray. If a list or unitless array is supplied, code units are
- assumed.
+ center : A sequence of floats, a string, or a tuple.
+ The coordinate of the center of the image. If set to 'c', 'center' or
+ left blank, the plot is centered on the middle of the domain. If set to
+ 'max' or 'm', the center will be located at the maximum of the
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
+ as a tuple containing a coordinate and string unit name or by passing
+ in a YTArray. If a list or unitless array is supplied, code units are
+ assumed.
width : tuple or a float.
Width can have four different formats to support windows with variable
x and y widths. They are:
diff -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 -r 2030b40ee37eb48bc0383a6e0cc844a97b5e0eab yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -960,13 +960,15 @@
or the axis name itself
fields : string
The name of the field(s) to be plotted.
- center : A sequence floats, a string, or a tuple.
+ center : A sequence of floats, a string, or a tuple.
The coordinate of the center of the image. If set to 'c', 'center' or
left blank, the plot is centered on the middle of the domain. If set to
'max' or 'm', the center will be located at the maximum of the
- ('gas', 'density') field. Units can be specified by passing in center
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
as a tuple containing a coordinate and string unit name or by passing
- in a YTArray. If a list or unitless array is supplied, code units are
+ in a YTArray. If a list or unitless array is supplied, code units are
assumed.
width : tuple or a float.
Width can have four different formats to support windows with variable
@@ -1081,13 +1083,15 @@
or the axis name itself
fields : string
The name of the field(s) to be plotted.
- center : A sequence floats, a string, or a tuple.
+ center : A sequence of floats, a string, or a tuple.
The coordinate of the center of the image. If set to 'c', 'center' or
left blank, the plot is centered on the middle of the domain. If set to
'max' or 'm', the center will be located at the maximum of the
- ('gas', 'density') field. Units can be specified by passing in center
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
as a tuple containing a coordinate and string unit name or by passing
- in a YTArray. If a list or unitless array is supplied, code units are
+ in a YTArray. If a list or unitless array is supplied, code units are
assumed.
width : tuple or a float.
Width can have four different formats to support windows with variable
@@ -1164,7 +1168,9 @@
"sum" : This method is the same as integrate, except that it does not
multiply by a path length when performing the integration, and is
- just a straight summation of the field along the given axis.
+ just a straight summation of the field along the given axis. WARNING:
+ This should only be used for uniform resolution grid datasets, as other
+ datasets may result in unphysical images.
proj_style : string
The method of projection--same as method keyword. Deprecated as of
version 3.0.2. Please use method instead.
@@ -1238,13 +1244,15 @@
The vector normal to the slicing plane.
fields : string
The name of the field(s) to be plotted.
- center : A sequence floats, a string, or a tuple.
+ center : A sequence of floats, a string, or a tuple.
The coordinate of the center of the image. If set to 'c', 'center' or
left blank, the plot is centered on the middle of the domain. If set to
'max' or 'm', the center will be located at the maximum of the
- ('gas', 'density') field. Units can be specified by passing in center
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
as a tuple containing a coordinate and string unit name or by passing
- in a YTArray. If a list or unitless array is supplied, code units are
+ in a YTArray. If a list or unitless array is supplied, code units are
assumed.
width : tuple or a float.
Width can have four different formats to support windows with variable
@@ -1349,13 +1357,15 @@
The vector normal to the slicing plane.
fields : string
The name of the field(s) to be plotted.
- center : A sequence floats, a string, or a tuple.
+ center : A sequence of floats, a string, or a tuple.
The coordinate of the center of the image. If set to 'c', 'center' or
left blank, the plot is centered on the middle of the domain. If set to
'max' or 'm', the center will be located at the maximum of the
- ('gas', 'density') field. Units can be specified by passing in center
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
as a tuple containing a coordinate and string unit name or by passing
- in a YTArray. If a list or unitless array is supplied, code units are
+ in a YTArray. If a list or unitless array is supplied, code units are
assumed.
width : tuple or a float.
Width can have four different formats to support windows with variable
@@ -1407,7 +1417,9 @@
"sum" : This method is the same as integrate, except that it does not
multiply by a path length when performing the integration, and is
- just a straight summation of the field along the given axis.
+ just a straight summation of the field along the given axis. WARNING:
+ This should only be used for uniform resolution grid datasets, as other
+ datasets may result in unphysical images.
"""
_plot_type = 'OffAxisProjection'
_frb_generator = OffAxisProjectionFixedResolutionBuffer
@@ -1767,9 +1779,11 @@
The coordinate of the center of the image. If set to 'c', 'center' or
left blank, the plot is centered on the middle of the domain. If set to
'max' or 'm', the center will be located at the maximum of the
- ('gas', 'density') field. Units can be specified by passing in center
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
as a tuple containing a coordinate and string unit name or by passing
- in a YTArray. If a list or unitless array is supplied, code units are
+ in a YTArray. If a list or unitless array is supplied, code units are
assumed.
width : tuple or a float.
Width can have four different formats to support windows with variable
diff -r 3d3bdd8cbf98314b60afe27a1159bfedd575a6f1 -r 2030b40ee37eb48bc0383a6e0cc844a97b5e0eab yt/visualization/volume_rendering/camera.py
--- a/yt/visualization/volume_rendering/camera.py
+++ b/yt/visualization/volume_rendering/camera.py
@@ -2400,7 +2400,9 @@
"sum" : This method is the same as integrate, except that it does not
multiply by a path length when performing the integration, and is
- just a straight summation of the field along the given axis.
+ just a straight summation of the field along the given axis. WARNING:
+ This should only be used for uniform resolution grid datasets, as other
+ datasets may result in unphysical images.
Returns
-------
https://bitbucket.org/yt_analysis/yt/commits/6c18ee5b5153/
Changeset: 6c18ee5b5153
Branch: yt
User: jzuhone
Date: 2014-11-04 01:44:45+00:00
Summary: Make sure plots can pick up latex prefixes
Affected #: 1 file
diff -r 2030b40ee37eb48bc0383a6e0cc844a97b5e0eab -r 6c18ee5b51532cb947ba0914db7445a6f135a4c2 yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -50,6 +50,8 @@
Unit
from yt.units.unit_registry import \
UnitParseError
+from yt.units.unit_lookup_table import \
+ prefixable_units, latex_prefixes
from yt.units.yt_array import \
YTArray, YTQuantity
from yt.utilities.png_writer import \
@@ -795,6 +797,11 @@
raise RuntimeError
if un in formatted_length_unit_names:
un = formatted_length_unit_names[un]
+ pp = un[0]
+ if pp in latex_prefixes:
+ symbol_wo_prefix = un[1:]
+ if symbol_wo_prefix in prefixable_units:
+ un = un.replace(pp, "{"+latex_prefixes[pp]+"}", 1)
if un not in ['1', 'u', 'unitary']:
if hinv:
un = un + '\,h^{-1}'
https://bitbucket.org/yt_analysis/yt/commits/a020d4fa702d/
Changeset: a020d4fa702d
Branch: yt
User: jzuhone
Date: 2014-11-04 02:01:31+00:00
Summary: Make sure we check for the projection method here.
Affected #: 1 file
diff -r 6c18ee5b51532cb947ba0914db7445a6f135a4c2 -r a020d4fa702dee264c18d270153426546aab003e yt/visualization/fixed_resolution.py
--- a/yt/visualization/fixed_resolution.py
+++ b/yt/visualization/fixed_resolution.py
@@ -420,7 +420,7 @@
no_ghost=dd.no_ghost, interpolated=dd.interpolated,
north_vector=dd.north_vector, method=dd.method)
units = Unit(dd.ds.field_info[item].units, registry=dd.ds.unit_registry)
- if dd.weight_field is None:
+ if dd.weight_field is None and dd.method == "integrate":
units *= Unit('cm', registry=dd.ds.unit_registry)
ia = ImageArray(buff.swapaxes(0,1), input_units=units, info=self._get_info(item))
self[item] = ia
https://bitbucket.org/yt_analysis/yt/commits/e99afa8b1ded/
Changeset: e99afa8b1ded
Branch: yt
User: jzuhone
Date: 2014-11-04 02:05:48+00:00
Summary: Make a warning here about how to use the sum projection method
Affected #: 1 file
diff -r a020d4fa702dee264c18d270153426546aab003e -r e99afa8b1ded63ffe8878b15f51ea418beb9e9cd doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -278,7 +278,8 @@
straight summation of the field along the given axis. The units of the
projected field will be the same as those of the unprojected field. This
method is typically only useful for datasets such as 3D FITS cubes where
- the third axis of the dataset is something like velocity or frequency.
+ the third axis of the dataset is something like velocity or frequency, and
+ should _only_ be used with fixed-resolution grid-based datasets.
.. _off-axis-projections:
https://bitbucket.org/yt_analysis/yt/commits/61710bdf11d6/
Changeset: 61710bdf11d6
Branch: yt
User: jzuhone
Date: 2014-11-04 02:13:06+00:00
Summary: Include information about the new centering options.
Affected #: 1 file
diff -r e99afa8b1ded63ffe8878b15f51ea418beb9e9cd -r 61710bdf11d68dd5695300fa0170bd3b2327e1b5 doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -124,11 +124,16 @@
slice. To instead use the coordinates as defined in the dataset, use
the optional argument: ``origin="native"``
-If supplied
-without units, the center is assumed by in code units. Optionally, you can
-supply 'c' or 'm' for the center. These two choices will center the plot on the
-center of the simulation box and the coordinate of the maximum density cell,
-respectively.
+If supplied without units, the center is assumed by in code units. There are also
+the following alternative options for the `center` keyword:
+
+* `"center"`, "c"`: the domain center
+* `"max"`, `"m"`: the position of the maximum density
+* `("min", field)`: the position of the minimum of `field`
+* `("max", field)`: the position of the maximum of `field`
+
+where for the last two objects any spatial field, such as `"density"`, `"velocity_z"`,
+etc., may be used, e.g. `center=("min","temperature")`
Here is an example that combines all of the options we just discussed.
@@ -279,7 +284,7 @@
projected field will be the same as those of the unprojected field. This
method is typically only useful for datasets such as 3D FITS cubes where
the third axis of the dataset is something like velocity or frequency, and
- should _only_ be used with fixed-resolution grid-based datasets.
+ should _only_ be used with fixed-resolution grid-based datasets.
.. _off-axis-projections:
https://bitbucket.org/yt_analysis/yt/commits/9ea4e1e6ac41/
Changeset: 9ea4e1e6ac41
Branch: yt
User: jzuhone
Date: 2014-11-04 14:27:41+00:00
Summary: Merge
Affected #: 6 files
diff -r 61710bdf11d68dd5695300fa0170bd3b2327e1b5 -r 9ea4e1e6ac41f14af6216052141a2f364f506112 doc/source/analyzing/objects.rst
--- a/doc/source/analyzing/objects.rst
+++ b/doc/source/analyzing/objects.rst
@@ -96,7 +96,7 @@
**Point**
| Class :class:`~yt.data_objects.selection_data_containers.YTPointBase`
- | Usage: ``point(coord, ds=None, field_parameters=None)``
+ | Usage: ``point(coord, ds=None, field_parameters=None, data_source=None)``
| A point defined by a single cell at specified coordinates.
1D Objects
@@ -104,14 +104,14 @@
**Ray (Axis-Aligned)**
| Class :class:`~yt.data_objects.selection_data_containers.YTOrthoRayBase`
- | Usage: ``ortho_ray(axis, coord, ds=None, field_parameters=None)``
+ | Usage: ``ortho_ray(axis, coord, ds=None, field_parameters=None, data_source=None)``
| A line (of data cells) stretching through the full domain
aligned with one of the x,y,z axes. Defined by an axis and a point
to be intersected.
**Ray (Arbitrarily-Aligned)**
| Class :class:`~yt.data_objects.selection_data_containers.YTRayBase`
- | Usage: ``ray(start_coord, end_coord, ds=None, field_parameters=None)``
+ | Usage: ``ray(start_coord, end_coord, ds=None, field_parameters=None, data_source=None)``
| A line (of data cells) defined by arbitrary start and end coordinates.
2D Objects
@@ -119,13 +119,13 @@
**Slice (Axis-Aligned)**
| Class :class:`~yt.data_objects.selection_data_containers.YTSliceBase`
- | Usage: ``slice(axis, coord, center=None, ds=None, field_parameters=None)``
+ | Usage: ``slice(axis, coord, center=None, ds=None, field_parameters=None, data_source=None)``
| A plane normal to one of the axes and intersecting a particular
coordinate.
**Slice (Arbitrarily-Aligned)**
| Class :class:`~yt.data_objects.selection_data_containers.YTCuttingPlaneBase`
- | Usage: ``cutting(normal, coord, north_vector=None, ds=None, field_parameters=None)``
+ | Usage: ``cutting(normal, coord, north_vector=None, ds=None, field_parameters=None, data_source=None)``
| A plane normal to a specified vector and intersecting a particular
coordinate.
@@ -141,8 +141,8 @@
**Box Region**
| Class :class:`~yt.data_objects.selection_data_containers.YTRegionBase`
- | Usage: ``region(center, left_edge, right_edge, fields=None, ds=None, field_parameters=None)``
- | Alternatively: ``box(left_edge, right_edge, fields=None, ds=None, field_parameters=None)``
+ | Usage: ``region(center, left_edge, right_edge, fields=None, ds=None, field_parameters=None, data_source=None)``
+ | Alternatively: ``box(left_edge, right_edge, fields=None, ds=None, field_parameters=None, data_source=None)``
| A box-like region aligned with the grid axis orientation. It is
defined by a left_edge, a right_edge, and a center. The left_edge
and right_edge are the minimum and maximum bounds in the three axes
@@ -152,14 +152,14 @@
**Disk/Cylinder**
| Class: :class:`~yt.data_objects.selection_data_containers.YTDiskBase`
- | Usage: ``disk(center, normal, radius, height, fields=None, ds=None, field_parameters=None)``
+ | Usage: ``disk(center, normal, radius, height, fields=None, ds=None, field_parameters=None, data_source=None)``
| A cylinder defined by a point at the center of one of the circular bases,
a normal vector to it defining the orientation of the length of the
cylinder, and radius and height values for the cylinder's dimensions.
**Ellipsoid**
| Class :class:`~yt.data_objects.selection_data_containers.YTEllipsoidBase`
- | Usage: ``ellipsoid(center, semi_major_axis_length, semi_medium_axis_length, semi_minor_axis_length, semi_major_vector, tilt, fields=None, ds=None, field_parameters=None)``
+ | Usage: ``ellipsoid(center, semi_major_axis_length, semi_medium_axis_length, semi_minor_axis_length, semi_major_vector, tilt, fields=None, ds=None, field_parameters=None, data_source=None)``
| An ellipsoid with axis magnitudes set by semi_major_axis_length,
semi_medium_axis_length, and semi_minor_axis_length. semi_major_vector
sets the direction of the semi_major_axis. tilt defines the orientation
@@ -167,7 +167,7 @@
**Sphere**
| Class :class:`~yt.data_objects.selection_data_containers.YTSphereBase`
- | Usage: ``sphere(center, radius, ds=None, field_parameters=None)``
+ | Usage: ``sphere(center, radius, ds=None, field_parameters=None, data_source=None)``
| A sphere defined by a central coordinate and a radius.
@@ -176,6 +176,12 @@
See also the section on :ref:`filtering-data`.
+**Intersecting Regions**
+ | Most Region objects provide a data_source parameter, which allows you to subselect
+ | one region from another (in the coordinate system of the DataSet). Note, this can
+ | easily lead to empty data for non-intersecting regions.
+ | Usage: ``slice(axis, coord, ds, data_source=sph)``
+
**Boolean Regions**
| **Note: not yet implemented in yt 3.0**
| Usage: ``boolean()``
diff -r 61710bdf11d68dd5695300fa0170bd3b2327e1b5 -r 9ea4e1e6ac41f14af6216052141a2f364f506112 yt/data_objects/construction_data_containers.py
--- a/yt/data_objects/construction_data_containers.py
+++ b/yt/data_objects/construction_data_containers.py
@@ -42,7 +42,7 @@
from yt.utilities.minimal_representation import \
MinimalProjectionData
from yt.utilities.parallel_tools.parallel_analysis_interface import \
- parallel_objects, parallel_root_only, ParallelAnalysisInterface
+ parallel_objects, parallel_root_only
from yt.units.unit_object import Unit
import yt.geometry.particle_deposit as particle_deposit
from yt.utilities.grid_data_format.writer import write_to_gdf
@@ -835,7 +835,7 @@
new_fields.append(output_field)
level_state.fields = new_fields
-class YTSurfaceBase(YTSelectionContainer3D, ParallelAnalysisInterface):
+class YTSurfaceBase(YTSelectionContainer3D):
r"""This surface object identifies isocontours on a cell-by-cell basis,
with no consideration of global connectedness, and returns the vertices
of the Triangles in that isocontour.
@@ -888,7 +888,6 @@
("index", "z"))
vertices = None
def __init__(self, data_source, surface_field, field_value):
- ParallelAnalysisInterface.__init__(self)
self.data_source = data_source
self.surface_field = surface_field
self.field_value = field_value
diff -r 61710bdf11d68dd5695300fa0170bd3b2327e1b5 -r 9ea4e1e6ac41f14af6216052141a2f364f506112 yt/data_objects/data_containers.py
--- a/yt/data_objects/data_containers.py
+++ b/yt/data_objects/data_containers.py
@@ -41,6 +41,8 @@
from yt.fields.derived_field import \
ValidateSpatial
import yt.geometry.selection_routines
+from yt.geometry.selection_routines import \
+ compose_selector
from yt.extern.six import add_metaclass
def force_array(item, shape):
@@ -101,8 +103,15 @@
sets its initial set of fields, and the remainder of the arguments
are passed as field_parameters.
"""
- if ds != None:
+ # ds is typically set in the new object type created in Dataset._add_object_class
+ # but it can also be passed as a parameter to the constructor, in which case it will
+ # override the default. This code ensures it is never not set.
+ if ds is not None:
self.ds = ds
+ else:
+ if not hasattr(self, "ds"):
+ raise RuntimeError("Error: ds must be set either through class type or parameter to the constructor")
+
self._current_particle_type = "all"
self._current_fluid_type = self.ds.default_fluid_type
self.ds.objects.append(weakref.proxy(self))
@@ -542,10 +551,22 @@
_sort_by = None
_selector = None
_current_chunk = None
+ _data_source = None
+ _dimensionality = None
- def __init__(self, *args, **kwargs):
- super(YTSelectionContainer, self).__init__(*args, **kwargs)
-
+ def __init__(self, ds, field_parameters, data_source=None):
+ ParallelAnalysisInterface.__init__(self)
+ super(YTSelectionContainer, self).__init__(ds, field_parameters)
+ self._data_source = data_source
+ if data_source is not None:
+ if data_source.ds is not self.ds:
+ raise RuntimeError("Attempted to construct a DataContainer with a data_source from a different DataSet", ds, data_source.ds)
+ else:
+ print "DataSets: ", self.ds, data_source.ds
+ if data_source._dimensionality < self._dimensionality:
+ raise RuntimeError("Attempted to construct a DataContainer with a data_source of lower dimensionality (%u vs %u)" %
+ (data_source._dimensionality, self._dimensionality))
+
@property
def selector(self):
if self._selector is not None: return self._selector
@@ -555,7 +576,11 @@
"%s_selector" % self._type_name, None)
if sclass is None:
raise YTDataSelectorNotImplemented(self._type_name)
- self._selector = sclass(self)
+
+ if self._data_source is not None:
+ self._selector = compose_selector(self, self._data_source.selector, sclass(self))
+ else:
+ self._selector = sclass(self)
return self._selector
def chunks(self, fields, chunking_style, **kwargs):
@@ -765,30 +790,32 @@
class YTSelectionContainer0D(YTSelectionContainer):
_spatial = False
- def __init__(self, ds, field_parameters):
+ _dimensionality = 0
+ def __init__(self, ds, field_parameters = None, data_source = None):
super(YTSelectionContainer0D, self).__init__(
- ds, field_parameters)
+ ds, field_parameters, data_source)
class YTSelectionContainer1D(YTSelectionContainer):
_spatial = False
- def __init__(self, ds, field_parameters):
+ _dimensionality = 1
+ def __init__(self, ds, field_parameters = None, data_source = None):
super(YTSelectionContainer1D, self).__init__(
- ds, field_parameters)
+ ds, field_parameters, data_source)
self._grids = None
self._sortkey = None
self._sorted = {}
class YTSelectionContainer2D(YTSelectionContainer):
_key_fields = ['px','py','pdx','pdy']
+ _dimensionality = 2
"""
Prepares the YTSelectionContainer2D, normal to *axis*. If *axis* is 4, we are not
aligned with any axis.
"""
_spatial = False
- def __init__(self, axis, ds, field_parameters):
- ParallelAnalysisInterface.__init__(self)
+ def __init__(self, axis, ds, field_parameters = None, data_source = None):
super(YTSelectionContainer2D, self).__init__(
- ds, field_parameters)
+ ds, field_parameters, data_source)
# We need the ds, which will exist by now, for fix_axis.
self.axis = fix_axis(axis, self.ds)
self.set_field_parameter("axis", axis)
@@ -910,9 +937,9 @@
_key_fields = ['x','y','z','dx','dy','dz']
_spatial = False
_num_ghost_zones = 0
- def __init__(self, center, ds = None, field_parameters = None):
- ParallelAnalysisInterface.__init__(self)
- super(YTSelectionContainer3D, self).__init__(ds, field_parameters)
+ _dimensionality = 3
+ def __init__(self, center, ds, field_parameters = None, data_source = None):
+ super(YTSelectionContainer3D, self).__init__(ds, field_parameters, data_source)
self._set_center(center)
self.coords = None
self._grids = None
@@ -1273,9 +1300,9 @@
"""
_type_name = "boolean"
_con_args = ("regions",)
- def __init__(self, regions, fields = None, ds = None, **kwargs):
+ def __init__(self, regions, fields = None, ds = None, field_parameters = None, data_source = None):
# Center is meaningless, but we'll define it all the same.
- YTSelectionContainer3D.__init__(self, [0.5]*3, fields, ds, **kwargs)
+ YTSelectionContainer3D.__init__(self, [0.5]*3, fields, ds, field_parameters, data_source)
self.regions = regions
self._all_regions = []
self._some_overlap = []
diff -r 61710bdf11d68dd5695300fa0170bd3b2327e1b5 -r 9ea4e1e6ac41f14af6216052141a2f364f506112 yt/data_objects/selection_data_containers.py
--- a/yt/data_objects/selection_data_containers.py
+++ b/yt/data_objects/selection_data_containers.py
@@ -51,8 +51,11 @@
ds: Dataset, optional
An optional dataset to use rather than self.ds
field_parameters : dictionary
- A dictionary of field parameters than can be accessed by derived
- fields.
+ A dictionary of field parameters than can be accessed by derived
+ fields.
+ data_source: optional
+ Draw the selection from the provided data source rather than
+ all data associated with the data_set
Examples
--------
@@ -64,8 +67,8 @@
"""
_type_name = "point"
_con_args = ('p',)
- def __init__(self, p, ds = None, field_parameters = None):
- super(YTPointBase, self).__init__(ds, field_parameters)
+ def __init__(self, p, ds=None, field_parameters=None, data_source=None):
+ super(YTPointBase, self).__init__(ds, field_parameters, data_source)
self.p = p
class YTOrthoRayBase(YTSelectionContainer1D):
@@ -92,6 +95,9 @@
field_parameters : dictionary
A dictionary of field parameters than can be accessed by derived
fields.
+ data_source: optional
+ Draw the selection from the provided data source rather than
+ all data associated with the data_set
Examples
--------
@@ -104,8 +110,9 @@
_key_fields = ['x','y','z','dx','dy','dz']
_type_name = "ortho_ray"
_con_args = ('axis', 'coords')
- def __init__(self, axis, coords, ds=None, field_parameters=None):
- super(YTOrthoRayBase, self).__init__(ds, field_parameters)
+ def __init__(self, axis, coords, ds=None,
+ field_parameters=None, data_source=None):
+ super(YTOrthoRayBase, self).__init__(ds, field_parameters, data_source)
self.axis = axis
xax = self.ds.coordinates.x_axis[self.axis]
yax = self.ds.coordinates.y_axis[self.axis]
@@ -144,6 +151,9 @@
field_parameters : dictionary
A dictionary of field parameters than can be accessed by derived
fields.
+ data_source: optional
+ Draw the selection from the provided data source rather than
+ all data associated with the data_set
Examples
--------
@@ -156,8 +166,9 @@
_type_name = "ray"
_con_args = ('start_point', 'end_point')
_container_fields = ("t", "dts")
- def __init__(self, start_point, end_point, ds=None, field_parameters=None):
- super(YTRayBase, self).__init__(ds, field_parameters)
+ def __init__(self, start_point, end_point, ds=None,
+ field_parameters=None, data_source=None):
+ super(YTRayBase, self).__init__(ds, field_parameters, data_source)
self.start_point = self.ds.arr(start_point,
'code_length', dtype='float64')
self.end_point = self.ds.arr(end_point,
@@ -204,6 +215,9 @@
field_parameters : dictionary
A dictionary of field parameters than can be accessed by derived
fields.
+ data_source: optional
+ Draw the selection from the provided data source rather than
+ all data associated with the data_set
Examples
--------
@@ -217,10 +231,10 @@
_type_name = "slice"
_con_args = ('axis', 'coord')
_container_fields = ("px", "py", "pdx", "pdy")
-
def __init__(self, axis, coord, center=None, ds=None,
- field_parameters = None):
- YTSelectionContainer2D.__init__(self, axis, ds, field_parameters)
+ field_parameters=None, data_source=None):
+ YTSelectionContainer2D.__init__(self, axis, ds,
+ field_parameters, data_source)
self._set_center(center)
self.coord = coord
@@ -285,6 +299,9 @@
field_parameters : dictionary
A dictionary of field parameters than can be accessed by derived
fields.
+ data_source: optional
+ Draw the selection from the provided data source rather than
+ all data associated with the data_set
Notes
-----
@@ -308,10 +325,10 @@
_type_name = "cutting"
_con_args = ('normal', 'center')
_container_fields = ("px", "py", "pz", "pdx", "pdy", "pdz")
-
- def __init__(self, normal, center, north_vector = None,
- ds = None, field_parameters = None):
- YTSelectionContainer2D.__init__(self, 4, ds, field_parameters)
+ def __init__(self, normal, center, north_vector=None,
+ ds=None, field_parameters=None, data_source=None):
+ YTSelectionContainer2D.__init__(self, 4, ds,
+ field_parameters, data_source)
self._set_center(center)
self.set_field_parameter('center',center)
# Let's set up our plane equation
@@ -465,7 +482,7 @@
Parameters
----------
- center : array_like
+ center : array_like
coordinate to which the normal, radius, and height all reference
normal : array_like
the normal vector defining the direction of lengthwise part of the
@@ -482,6 +499,9 @@
field_parameters : dictionary
A dictionary of field parameters than can be accessed by derived
fields.
+ data_source: optional
+ Draw the selection from the provided data source rather than
+ all data associated with the data_set
Examples
--------
@@ -494,8 +514,9 @@
_type_name = "disk"
_con_args = ('center', '_norm_vec', 'radius', 'height')
def __init__(self, center, normal, radius, height, fields=None,
- ds=None, **kwargs):
- YTSelectionContainer3D.__init__(self, center, fields, ds, **kwargs)
+ ds=None, field_parameters=None, data_source=None):
+ YTSelectionContainer3D.__init__(self, center, ds,
+ field_parameters, data_source)
self._norm_vec = np.array(normal)/np.sqrt(np.dot(normal,normal))
self.set_field_parameter("normal", self._norm_vec)
self.set_field_parameter("center", self.center)
@@ -523,9 +544,10 @@
"""
_type_name = "region"
_con_args = ('center', 'left_edge', 'right_edge')
- def __init__(self, center, left_edge, right_edge, fields = None,
- ds = None, **kwargs):
- YTSelectionContainer3D.__init__(self, center, ds, **kwargs)
+ def __init__(self, center, left_edge, right_edge, fields=None,
+ ds=None, field_parameters=None, data_source=None):
+ YTSelectionContainer3D.__init__(self, center, ds,
+ field_parameters, data_source)
if not isinstance(left_edge, YTArray):
self.left_edge = self.ds.arr(left_edge, 'code_length')
else:
@@ -542,8 +564,10 @@
"""
_type_name = "data_collection"
_con_args = ("_obj_list",)
- def __init__(self, obj_list, ds=None, field_parameters=None, center=None):
- YTSelectionContainer3D.__init__(self, center, ds, field_parameters)
+ def __init__(self, obj_list, ds=None, field_parameters=None,
+ data_source=None, center=None):
+ YTSelectionContainer3D.__init__(self, center, ds,
+ field_parameters, data_source)
self._obj_ids = np.array([o.id - o._id_offset for o in obj_list],
dtype="int64")
self._obj_list = obj_list
@@ -569,8 +593,10 @@
"""
_type_name = "sphere"
_con_args = ('center', 'radius')
- def __init__(self, center, radius, ds = None, field_parameters = None):
- super(YTSphereBase, self).__init__(center, ds, field_parameters)
+ def __init__(self, center, radius, ds=None,
+ field_parameters=None, data_source=None):
+ super(YTSphereBase, self).__init__(center, ds,
+ field_parameters, data_source)
# Unpack the radius, if necessary
radius = fix_length(radius, self.ds)
if radius < self.index.get_smallest_dx():
@@ -615,8 +641,9 @@
_type_name = "ellipsoid"
_con_args = ('center', '_A', '_B', '_C', '_e0', '_tilt')
def __init__(self, center, A, B, C, e0, tilt, fields=None,
- ds=None, field_parameters = None):
- YTSelectionContainer3D.__init__(self, center, ds, field_parameters)
+ ds=None, field_parameters=None, data_source=None):
+ YTSelectionContainer3D.__init__(self, center, ds,
+ field_parameters, data_source)
# make sure the magnitudes of semi-major axes are in order
if A<B or B<C:
raise YTEllipsoidOrdering(ds, A, B, C)
@@ -625,10 +652,10 @@
self._B = self.ds.quan(B, 'code_length')
self._C = self.ds.quan(C, 'code_length')
if self._C < self.index.get_smallest_dx():
- raise YTSphereTooSmall(ds, self._C, self.index.get_smallest_dx())
+ raise YTSphereTooSmall(self.ds, self._C, self.index.get_smallest_dx())
self._e0 = e0 = e0 / (e0**2.0).sum()**0.5
self._tilt = tilt
-
+
# find the t1 angle needed to rotate about z axis to align e0 to x
t1 = np.arctan(e0[1] / e0[0])
# rotate e0 by -t1
@@ -684,9 +711,10 @@
"""
_type_name = "cut_region"
_con_args = ("base_object", "conditionals")
- def __init__(self, base_object, conditionals, ds = None,
- field_parameters = None):
- super(YTCutRegionBase, self).__init__(base_object.center, ds, field_parameters)
+ def __init__(self, base_object, conditionals, ds=None,
+ field_parameters=None, data_source=None):
+ super(YTCutRegionBase, self).__init__(base_object.center, ds,
+ field_parameters, data_source)
self.conditionals = ensure_list(conditionals)
self.base_object = base_object
self._selector = None
@@ -762,4 +790,3 @@
@property
def fwidth(self):
return self.base_object.fwidth[self._cond_ind,:]
-
diff -r 61710bdf11d68dd5695300fa0170bd3b2327e1b5 -r 9ea4e1e6ac41f14af6216052141a2f364f506112 yt/data_objects/tests/test_compose.py
--- /dev/null
+++ b/yt/data_objects/tests/test_compose.py
@@ -0,0 +1,146 @@
+from yt.testing import *
+from yt.fields.local_fields import add_field
+from yt.units.yt_array import YTArray, uintersect1d
+
+def setup():
+ from yt.config import ytcfg
+ ytcfg["yt","__withintesting"] = "True"
+
+# Copied from test_boolean for computing a unique identifier for
+# each cell from cell positions
+def _IDFIELD(field, data):
+ width = data.ds.domain_right_edge - data.ds.domain_left_edge
+ min_dx = YTArray(1.0/8192, input_units='code_length',
+ registry=data.ds.unit_registry)
+ delta = width / min_dx
+ x = data['x'] - min_dx / 2.
+ y = data['y'] - min_dx / 2.
+ z = data['z'] - min_dx / 2.
+ xi = x / min_dx
+ yi = y / min_dx
+ zi = z / min_dx
+ index = xi + delta[0] * (yi + delta[1] * zi)
+ index = index.astype('int64')
+ return index
+
+def test_compose_no_overlap():
+ r"""Test to make sure that composed data objects that don't
+ overlap behave the way we expect (return empty collections)
+ """
+ empty = np.array([])
+ for n in [1, 2, 4, 8]:
+ ds = fake_random_ds(64, nprocs=n)
+ ds.add_field("ID", function=_IDFIELD)
+
+ # position parameters for initial region
+ center = [0.25]*3
+ left_edge = [0.1]*3
+ right_edge = [0.4]*3
+ normal = [1, 0, 0]
+ radius = height = 0.15
+
+ # initial 3D regions
+ sources = [ds.sphere(center, radius),
+ ds.region(center, left_edge, right_edge),
+ ds.disk(center, normal, radius, height)]
+
+ # position parameters for non-overlapping regions
+ center = [0.75]*3
+ left_edge = [0.6]*3
+ right_edge = [0.9]*3
+
+ # subselect non-overlapping 0, 1, 2, 3D regions
+ for data1 in sources:
+ data2 = ds.sphere(center, radius, data_source=data1)
+ yield assert_array_equal, data2['ID'], empty
+
+ data2 = ds.region(center, left_edge, right_edge, data_source=data1)
+ yield assert_array_equal, data2['ID'], empty
+
+ data2 = ds.disk(center, normal, radius, height, data_source=data1)
+ yield assert_array_equal, data2['ID'], empty
+
+ for d in range(3):
+ data2 = ds.slice(d, center[d], data_source=data1)
+ yield assert_array_equal, data2['ID'], empty
+
+ for d in range(3):
+ data2 = ds.ortho_ray(d, center[0:d] + center[d+1:], data_source=data1)
+ yield assert_array_equal, data2['ID'], empty
+
+ data2 = ds.point(center, data_source=data1)
+ yield assert_array_equal, data2['ID'], empty
+
+def test_compose_overlap():
+ r"""Test to make sure that composed data objects that do
+ overlap behave the way we expect
+ """
+ empty = np.array([])
+ for n in [1, 2, 4, 8]:
+ ds = fake_random_ds(64, nprocs=n)
+ ds.add_field("ID", function=_IDFIELD)
+
+ # position parameters for initial region
+ center = [0.4, 0.5, 0.5]
+ left_edge = [0.1]*3
+ right_edge = [0.7]*3
+ normal = [1, 0, 0]
+ radius = height = 0.15
+
+ # initial 3D regions
+ sources = [ds.sphere(center, radius),
+ ds.region(center, left_edge, right_edge),
+ ds.disk(center, normal, radius, height)]
+
+ # position parameters for overlapping regions
+ center = [0.6, 0.5, 0.5]
+ left_edge = [0.3]*3
+ right_edge = [0.9]*3
+
+ # subselect non-overlapping 0, 1, 2, 3D regions
+ for data1 in sources:
+ id1 = data1['ID']
+
+ data2 = ds.sphere(center, radius)
+ data3 = ds.sphere(center, radius, data_source=data1)
+ id2 = data2['ID']
+ id3 = data3['ID']
+ id3.sort()
+ yield assert_array_equal, uintersect1d(id1, id2), id3
+
+ data2 = ds.region(center, left_edge, right_edge)
+ data3 = ds.region(center, left_edge, right_edge, data_source=data1)
+ id2 = data2['ID']
+ id3 = data3['ID']
+ id3.sort()
+ yield assert_array_equal, uintersect1d(id1, id2), id3
+
+ data2 = ds.disk(center, normal, radius, height)
+ data3 = ds.disk(center, normal, radius, height, data_source=data1)
+ id2 = data2['ID']
+ id3 = data3['ID']
+ id3.sort()
+ yield assert_array_equal, uintersect1d(id1, id2), id3
+
+ for d in range(3):
+ data2 = ds.slice(d, center[d])
+ data3 = ds.slice(d, center[d], data_source=data1)
+ id2 = data2['ID']
+ id3 = data3['ID']
+ id3.sort()
+ yield assert_array_equal, uintersect1d(id1, id2), id3
+
+ for d in range(3):
+ data2 = ds.ortho_ray(d, center[0:d] + center[d+1:])
+ data3 = ds.ortho_ray(d, center[0:d] + center[d+1:], data_source=data1)
+ id2 = data2['ID']
+ id3 = data3['ID']
+ id3.sort()
+ yield assert_array_equal, uintersect1d(id1, id2), id3
+
+ data2 = ds.point(center)
+ data3 = ds.point(center, data_source=data1)
+ id2 = data2['ID']
+ id3 = data3['ID']
+ id3.sort()
+ yield assert_array_equal, uintersect1d(id1, id2), id3
diff -r 61710bdf11d68dd5695300fa0170bd3b2327e1b5 -r 9ea4e1e6ac41f14af6216052141a2f364f506112 yt/geometry/selection_routines.pyx
--- a/yt/geometry/selection_routines.pyx
+++ b/yt/geometry/selection_routines.pyx
@@ -112,7 +112,7 @@
cdef class SelectorObject:
- def __cinit__(self, dobj):
+ def __cinit__(self, dobj, *args):
self.min_level = getattr(dobj, "min_level", 0)
self.max_level = getattr(dobj, "max_level", 99)
self.overlap_cells = 0
@@ -1726,6 +1726,65 @@
always_selector = AlwaysSelector
+cdef class ComposeSelector(SelectorObject):
+ cdef SelectorObject selector1
+ cdef SelectorObject selector2
+
+ def __init__(self, dobj, selector1, selector2):
+ self.selector1 = selector1
+ self.selector2 = selector2
+
+ def select_grids(self,
+ np.ndarray[np.float64_t, ndim=2] left_edges,
+ np.ndarray[np.float64_t, ndim=2] right_edges,
+ np.ndarray[np.int32_t, ndim=2] levels):
+ return np.logical_or(
+ self.selector1.select_grids(left_edges, right_edges, levels),
+ self.selector2.select_grids(left_edges, right_edges, levels))
+
+ cdef int select_cell(self, np.float64_t pos[3], np.float64_t dds[3]) nogil:
+ if self.selector1.select_cell(pos, dds) and \
+ self.selector2.select_cell(pos, dds):
+ return 1
+ else:
+ return 0
+
+ cdef int select_grid(self, np.float64_t left_edge[3],
+ np.float64_t right_edge[3], np.int32_t level,
+ Oct *o = NULL) nogil:
+ if self.selector1.select_grid(left_edge, right_edge, level, o) or \
+ self.selector2.select_grid(left_edge, right_edge, level, o):
+ return 1
+ else:
+ return 0
+
+ cdef int select_point(self, np.float64_t pos[3]) nogil:
+ if self.selector1.select_point(pos) and \
+ self.selector2.select_point(pos):
+ return 1
+ else:
+ return 0
+
+ cdef int select_sphere(self, np.float64_t pos[3], np.float64_t radius) nogil:
+ if self.selector1.select_sphere(pos, radius) and \
+ self.selector2.select_sphere(pos, radius):
+ return 1
+ else:
+ return 0
+
+ cdef int select_bbox(self, np.float64_t left_edge[3],
+ np.float64_t right_edge[3]) nogil:
+ if self.selector1.select_bbox(left_edge, right_edge) and \
+ self.selector2.select_bbox(left_edge, right_edge):
+ return 1
+ else:
+ return 0
+
+ def _hash_vals(self):
+ return (hash(self.selector1), hash(self.selector2))
+
+compose_selector = ComposeSelector
+
cdef class HaloParticlesSelector(SelectorObject):
cdef public object base_source
cdef SelectorObject base_selector
https://bitbucket.org/yt_analysis/yt/commits/52a7220cf325/
Changeset: 52a7220cf325
Branch: yt
User: jzuhone
Date: 2014-11-05 01:46:24+00:00
Summary: Bugfix
Affected #: 1 file
diff -r 9ea4e1e6ac41f14af6216052141a2f364f506112 -r 52a7220cf325116928457893a2ee1810407331e0 yt/analysis_modules/sunyaev_zeldovich/projection.py
--- a/yt/analysis_modules/sunyaev_zeldovich/projection.py
+++ b/yt/analysis_modules/sunyaev_zeldovich/projection.py
@@ -148,7 +148,7 @@
self.ds.add_field(("gas","beta_par"), function=beta_par, units="g/cm**3")
setup_sunyaev_zeldovich_fields(self.ds)
proj = self.ds.proj("density", axis, center=ctr, data_source=source)
- frb = proj.to_frb(width, nx)
+ frb = proj.to_frb(width[0], nx, height=width[1])
dens = frb["density"]
Te = frb["t_sz"]/dens
bpar = frb["beta_par"]/dens
https://bitbucket.org/yt_analysis/yt/commits/e433a9791f5d/
Changeset: e433a9791f5d
Branch: yt
User: MatthewTurk
Date: 2014-11-06 15:33:31+00:00
Summary: Merged in jzuhone/yt (pull request #1280)
Improved FITS support, with a number of small additional refinements
Affected #: 20 files
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de doc/source/analyzing/analysis_modules/PPVCube.ipynb
--- a/doc/source/analyzing/analysis_modules/PPVCube.ipynb
+++ b/doc/source/analyzing/analysis_modules/PPVCube.ipynb
@@ -1,7 +1,7 @@
{
"metadata": {
"name": "",
- "signature": "sha256:56a8d72735e3cc428ff04b241d4b2ce6f653019818c6fc7a4148840d99030c85"
+ "signature": "sha256:b83e125278c2e58da4d99ac9d2ca2a136d01f1094e1b83497925e0f9b9b056c2"
},
"nbformat": 3,
"nbformat_minor": 0,
@@ -32,7 +32,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "To demonstrate this functionality, we'll create a simple unigrid dataset from scratch of a rotating disk galaxy. We create a thin disk in the x-y midplane of the domain of three cells in height in either direction, and a radius of 10 kpc. The density and azimuthal velocity profiles of the disk as a function of radius will be given by the following functions:"
+ "To demonstrate this functionality, we'll create a simple unigrid dataset from scratch of a rotating disk. We create a thin disk in the x-y midplane of the domain of three cells in height in either direction, and a radius of 10 kpc. The density and azimuthal velocity profiles of the disk as a function of radius will be given by the following functions:"
]
},
{
@@ -84,7 +84,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Second, we'll construct the data arrays for the density and the velocity of the disk. Since we have the tangential velocity profile, we have to use the polar coordinates we derived earlier to compute `velx` and `vely`. Everywhere outside the disk, all fields are set to zero. "
+ "Second, we'll construct the data arrays for the density, temperature, and velocity of the disk. Since we have the tangential velocity profile, we have to use the polar coordinates we derived earlier to compute `velx` and `vely`. Everywhere outside the disk, all fields are set to zero. "
]
},
{
@@ -93,12 +93,15 @@
"input": [
"dens = np.zeros((nx,ny,nz))\n",
"dens[:,:,nz/2-3:nz/2+3] = (r**alpha).reshape(nx,ny,1) # the density profile of the disk\n",
- "vel_theta = r/(1.+(r/r_0)**beta) # the azimuthal velocity profile of the disk\n",
+ "temp = np.zeros((nx,ny,nz))\n",
+ "temp[:,:,nz/2-3:nz/2+3] = 1.0e5 # Isothermal\n",
+ "vel_theta = 100.*r/(1.+(r/r_0)**beta) # the azimuthal velocity profile of the disk\n",
"velx = np.zeros((nx,ny,nz))\n",
"vely = np.zeros((nx,ny,nz))\n",
"velx[:,:,nz/2-3:nz/2+3] = (-vel_theta*np.sin(theta)).reshape(nx,ny,1) # convert polar to cartesian\n",
"vely[:,:,nz/2-3:nz/2+3] = (vel_theta*np.cos(theta)).reshape(nx,ny,1) # convert polar to cartesian\n",
"dens[r > R] = 0.0\n",
+ "temp[r > R] = 0.0\n",
"velx[r > R] = 0.0\n",
"vely[r > R] = 0.0"
],
@@ -119,6 +122,7 @@
"input": [
"data = {}\n",
"data[\"density\"] = (dens,\"g/cm**3\")\n",
+ "data[\"temperature\"] = (temp, \"K\")\n",
"data[\"velocity_x\"] = (velx, \"km/s\")\n",
"data[\"velocity_y\"] = (vely, \"km/s\")\n",
"data[\"velocity_z\"] = (np.zeros((nx,ny,nz)), \"km/s\") # zero velocity in the z-direction\n",
@@ -189,7 +193,7 @@
"cell_type": "code",
"collapsed": false,
"input": [
- "cube = PPVCube(ds, L, \"density\", dims=(200,100,50), velocity_bounds=(-1.5,1.5,\"km/s\"))"
+ "cube = PPVCube(ds, L, \"density\", dims=(200,100,50), velocity_bounds=(-150.,150.,\"km/s\"))"
],
"language": "python",
"metadata": {},
@@ -199,14 +203,33 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Following this, we can now write this cube to a FITS file:"
+ "Following this, we can now write this cube to a FITS file. The x and y axes of the file can be in length units, which can be optionally specified by `length_unit`:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
- "cube.write_fits(\"cube.fits\", clobber=True, length_unit=(5.0,\"deg\"))"
+ "cube.write_fits(\"cube.fits\", clobber=True, length_unit=\"kpc\")"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Or one can use the `sky_scale` and `sky_center` keywords to set up the coordinates in RA and Dec:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "sky_scale = (1.0, \"arcsec/kpc\")\n",
+ "sky_center = (30., 45.) # RA, Dec in degrees\n",
+ "cube.write_fits(\"cube_sky.fits\", clobber=True, sky_scale=sky_scale, sky_center=sky_center)"
],
"language": "python",
"metadata": {},
@@ -223,7 +246,7 @@
"cell_type": "code",
"collapsed": false,
"input": [
- "ds = yt.load(\"cube.fits\")"
+ "ds_cube = yt.load(\"cube.fits\")"
],
"language": "python",
"metadata": {},
@@ -234,7 +257,7 @@
"collapsed": false,
"input": [
"# Specifying no center gives us the center slice\n",
- "slc = yt.SlicePlot(ds, \"z\", [\"density\"])\n",
+ "slc = yt.SlicePlot(ds_cube, \"z\", [\"density\"])\n",
"slc.show()"
],
"language": "python",
@@ -247,9 +270,9 @@
"input": [
"import yt.units as u\n",
"# Picking different velocities for the slices\n",
- "new_center = ds.domain_center\n",
- "new_center[2] = ds.spec2pixel(-1.0*u.km/u.s)\n",
- "slc = yt.SlicePlot(ds, \"z\", [\"density\"], center=new_center)\n",
+ "new_center = ds_cube.domain_center\n",
+ "new_center[2] = ds_cube.spec2pixel(-100.*u.km/u.s)\n",
+ "slc = yt.SlicePlot(ds_cube, \"z\", [\"density\"], center=new_center)\n",
"slc.show()"
],
"language": "python",
@@ -260,8 +283,8 @@
"cell_type": "code",
"collapsed": false,
"input": [
- "new_center[2] = ds.spec2pixel(0.7*u.km/u.s)\n",
- "slc = yt.SlicePlot(ds, \"z\", [\"density\"], center=new_center)\n",
+ "new_center[2] = ds_cube.spec2pixel(70.0*u.km/u.s)\n",
+ "slc = yt.SlicePlot(ds_cube, \"z\", [\"density\"], center=new_center)\n",
"slc.show()"
],
"language": "python",
@@ -272,8 +295,8 @@
"cell_type": "code",
"collapsed": false,
"input": [
- "new_center[2] = ds.spec2pixel(-0.3*u.km/u.s)\n",
- "slc = yt.SlicePlot(ds, \"z\", [\"density\"], center=new_center)\n",
+ "new_center[2] = ds_cube.spec2pixel(-30.0*u.km/u.s)\n",
+ "slc = yt.SlicePlot(ds_cube, \"z\", [\"density\"], center=new_center)\n",
"slc.show()"
],
"language": "python",
@@ -291,7 +314,7 @@
"cell_type": "code",
"collapsed": false,
"input": [
- "prj = yt.ProjectionPlot(ds, \"z\", [\"density\"], method=\"sum\")\n",
+ "prj = yt.ProjectionPlot(ds_cube, \"z\", [\"density\"], method=\"sum\")\n",
"prj.set_log(\"density\", True)\n",
"prj.set_zlim(\"density\", 1.0e-3, 0.2)\n",
"prj.show()"
@@ -299,9 +322,100 @@
"language": "python",
"metadata": {},
"outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The `thermal_broad` keyword allows one to simulate thermal line broadening based on the temperature, and the `atomic_weight` argument is used to specify the atomic weight of the particle that is doing the emitting."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "cube2 = PPVCube(ds, L, \"density\", dims=(200,100,50), velocity_bounds=(-150,150,\"km/s\"), thermal_broad=True, \n",
+ " atomic_weight=12.0)\n",
+ "cube2.write_fits(\"cube2.fits\", clobber=True, length_unit=\"kpc\")"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Taking a slice of this cube shows:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "ds_cube2 = yt.load(\"cube2.fits\")\n",
+ "new_center = ds_cube2.domain_center\n",
+ "new_center[2] = ds_cube2.spec2pixel(70.0*u.km/u.s)\n",
+ "slc = yt.SlicePlot(ds_cube2, \"z\", [\"density\"], center=new_center)\n",
+ "slc.show()"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "new_center[2] = ds_cube2.spec2pixel(-100.*u.km/u.s)\n",
+ "slc = yt.SlicePlot(ds_cube2, \"z\", [\"density\"], center=new_center)\n",
+ "slc.show()"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "where we can see the emission has been smeared into this velocity slice from neighboring slices due to the thermal broadening. \n",
+ "\n",
+ "Finally, the \"velocity\" or \"spectral\" axis of the cube can be changed to a different unit, such as wavelength, frequency, or energy: "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "print cube2.vbins[0], cube2.vbins[-1]\n",
+ "cube2.transform_spectral_axis(400.0,\"nm\")\n",
+ "print cube2.vbins[0], cube2.vbins[-1]"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If a FITS file is now written from the cube, the spectral axis will be in the new units. To reset the spectral axis back to the original velocity units:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "cube2.reset_spectral_axis()\n",
+ "print cube2.vbins[0], cube2.vbins[-1]"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
}
],
"metadata": {}
}
]
-}
+}
\ No newline at end of file
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -124,11 +124,16 @@
slice. To instead use the coordinates as defined in the dataset, use
the optional argument: ``origin="native"``
-If supplied
-without units, the center is assumed by in code units. Optionally, you can
-supply 'c' or 'm' for the center. These two choices will center the plot on the
-center of the simulation box and the coordinate of the maximum density cell,
-respectively.
+If supplied without units, the center is assumed by in code units. There are also
+the following alternative options for the `center` keyword:
+
+* `"center"`, "c"`: the domain center
+* `"max"`, `"m"`: the position of the maximum density
+* `("min", field)`: the position of the minimum of `field`
+* `("max", field)`: the position of the maximum of `field`
+
+where for the last two objects any spatial field, such as `"density"`, `"velocity_z"`,
+etc., may be used, e.g. `center=("min","temperature")`
Here is an example that combines all of the options we just discussed.
@@ -278,7 +283,8 @@
straight summation of the field along the given axis. The units of the
projected field will be the same as those of the unprojected field. This
method is typically only useful for datasets such as 3D FITS cubes where
- the third axis of the dataset is something like velocity or frequency.
+ the third axis of the dataset is something like velocity or frequency, and
+ should _only_ be used with fixed-resolution grid-based datasets.
.. _off-axis-projections:
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de yt/analysis_modules/ppv_cube/ppv_cube.py
--- a/yt/analysis_modules/ppv_cube/ppv_cube.py
+++ b/yt/analysis_modules/ppv_cube/ppv_cube.py
@@ -13,40 +13,81 @@
import numpy as np
from yt.utilities.on_demand_imports import _astropy
from yt.utilities.orientation import Orientation
-from yt.utilities.fits_image import FITSImageBuffer
+from yt.utilities.fits_image import FITSImageBuffer, sanitize_fits_unit
from yt.visualization.volume_rendering.camera import off_axis_projection
from yt.funcs import get_pbar
+from yt.utilities.physical_constants import clight, mh
+import yt.units.dimensions as ytdims
+from yt.units.yt_array import YTQuantity
+from yt.funcs import iterable
+from yt.utilities.parallel_tools.parallel_analysis_interface import \
+ parallel_root_only, parallel_objects
+import re
+import ppv_utils
+from yt.funcs import is_root
-def create_vlos(z_hat):
- def _v_los(field, data):
- vz = data["velocity_x"]*z_hat[0] + \
- data["velocity_y"]*z_hat[1] + \
- data["velocity_z"]*z_hat[2]
- return -vz
+def create_vlos(normal):
+ if isinstance(normal, basestring):
+ def _v_los(field, data):
+ return -data["velocity_%s" % normal]
+ else:
+ orient = Orientation(normal)
+ los_vec = orient.unit_vectors[2]
+ def _v_los(field, data):
+ vz = data["velocity_x"]*los_vec[0] + \
+ data["velocity_y"]*los_vec[1] + \
+ data["velocity_z"]*los_vec[2]
+ return -vz
return _v_los
+fits_info = {"velocity":("m/s","VELOCITY","v"),
+ "frequency":("Hz","FREQUENCY","f"),
+ "energy":("eV","ENERGY","E"),
+ "wavelength":("angstrom","WAVELENG","lambda")}
+
class PPVCube(object):
- def __init__(self, ds, normal, field, width=(1.0,"unitary"),
- dims=(100,100,100), velocity_bounds=None):
+ def __init__(self, ds, normal, field, center="c", width=(1.0,"unitary"),
+ dims=(100,100,100), velocity_bounds=None, thermal_broad=False,
+ atomic_weight=56., method="integrate"):
r""" Initialize a PPVCube object.
Parameters
----------
ds : dataset
The dataset.
- normal : array_like
- The normal vector along with to make the projections.
+ normal : array_like or string
+ The normal vector along with to make the projections. If an array, it
+ will be normalized. If a string, it will be assumed to be along one of the
+ principal axes of the domain ("x","y", or "z").
field : string
The field to project.
- width : float or tuple, optional
- The width of the projection in length units. Specify a float
- for code_length units or a tuple (value, units).
+ center : A sequence of floats, a string, or a tuple.
+ The coordinate of the center of the image. If set to 'c', 'center' or
+ left blank, the plot is centered on the middle of the domain. If set to
+ 'max' or 'm', the center will be located at the maximum of the
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
+ as a tuple containing a coordinate and string unit name or by passing
+ in a YTArray. If a list or unitless array is supplied, code units are
+ assumed.
+ width : float, tuple, or YTQuantity.
+ The width of the projection. A float will assume the width is in code units.
+ A (value, unit) tuple or YTQuantity allows for the units of the width to be
+ specified.
dims : tuple, optional
A 3-tuple of dimensions (nx,ny,nv) for the cube.
velocity_bounds : tuple, optional
A 3-tuple of (vmin, vmax, units) for the velocity bounds to
integrate over. If None, the largest velocity of the
dataset will be used, e.g. velocity_bounds = (-v.max(), v.max())
+ atomic_weight : float, optional
+ Set this value to the atomic weight of the particle that is emitting the line
+ if *thermal_broad* is True. Defaults to 56 (Fe).
+ method : string, optional
+ Set the projection method to be used.
+ "integrate" : line of sight integration over the line element.
+ "sum" : straight summation over the line of sight.
Examples
--------
@@ -55,21 +96,22 @@
>>> cube = PPVCube(ds, L, "density", width=(10.,"kpc"),
... velocity_bounds=(-5.,4.,"km/s"))
"""
+
self.ds = ds
self.field = field
self.width = width
+ self.particle_mass = atomic_weight*mh
+ self.thermal_broad = thermal_broad
+
+ self.center = ds.coordinates.sanitize_center(center, normal)[0]
self.nx = dims[0]
self.ny = dims[1]
self.nv = dims[2]
- normal = np.array(normal)
- normal /= np.sqrt(np.dot(normal, normal))
- vecs = np.identity(3)
- t = np.cross(normal, vecs).sum(axis=1)
- ax = t.argmax()
- north = np.cross(normal, vecs[ax,:]).ravel()
- orient = Orientation(normal, north_vector=north)
+ if method not in ["integrate","sum"]:
+ raise RuntimeError("Only the 'integrate' and 'sum' projection +"
+ "methods are supported in PPVCube.")
dd = ds.all_data()
@@ -85,27 +127,104 @@
ds.quan(velocity_bounds[1], velocity_bounds[2]))
self.vbins = np.linspace(self.v_bnd[0], self.v_bnd[1], num=self.nv+1)
+ self._vbins = self.vbins.copy()
self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
- self.dv = (self.v_bnd[1]-self.v_bnd[0])/self.nv
+ self.vmid_cgs = self.vmid.in_cgs().v
+ self.dv = self.vbins[1]-self.vbins[0]
+ self.dv_cgs = self.dv.in_cgs().v
- _vlos = create_vlos(orient.unit_vectors[2])
- ds.field_info.add_field(("gas","v_los"), function=_vlos, units="cm/s")
+ self.current_v = 0.0
- self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.field_units)
+ _vlos = create_vlos(normal)
+ self.ds.add_field(("gas","v_los"), function=_vlos, units="cm/s")
+
+ _intensity = self.create_intensity()
+ self.ds.add_field(("gas","intensity"), function=_intensity, units=self.field_units)
+
+ if method == "integrate":
+ self.proj_units = str(ds.quan(1.0, self.field_units+"*cm").units)
+ elif method == "sum":
+ self.proj_units = self.field_units
+
+ self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.proj_units)
+ storage = {}
pbar = get_pbar("Generating cube.", self.nv)
- for i in xrange(self.nv):
- _intensity = self._create_intensity(i)
- ds.add_field(("gas","intensity"), function=_intensity, units=self.field_units)
- prj = off_axis_projection(ds, ds.domain_center, normal, width,
- (self.nx, self.ny), "intensity")
- self.data[:,:,i] = prj[:,:]
- ds.field_info.pop(("gas","intensity"))
+ for sto, i in parallel_objects(xrange(self.nv), storage=storage):
+ self.current_v = self.vmid_cgs[i]
+ if isinstance(normal, basestring):
+ prj = ds.proj("intensity", ds.coordinates.axis_id[normal], method=method)
+ buf = prj.to_frb(width, self.nx, center=self.center)["intensity"]
+ else:
+ buf = off_axis_projection(ds, self.center, normal, width,
+ (self.nx, self.ny), "intensity",
+ no_ghost=True, method=method)[::-1]
+ sto.result_id = i
+ sto.result = buf
pbar.update(i)
-
pbar.finish()
- def write_fits(self, filename, clobber=True, length_unit=(10.0, "kpc"),
- sky_center=(30.,45.)):
+ self.data = ds.arr(np.zeros((self.nx,self.ny,self.nv)), self.proj_units)
+ if is_root():
+ for i, buf in sorted(storage.items()):
+ self.data[:,:,i] = buf[:,:]
+
+ self.axis_type = "velocity"
+
+ # Now fix the width
+ if iterable(self.width):
+ self.width = ds.quan(self.width[0], self.width[1])
+ elif isinstance(self.width, YTQuantity):
+ self.width = width
+ else:
+ self.width = ds.quan(self.width, "code_length")
+
+ def create_intensity(self):
+ def _intensity(field, data):
+ v = self.current_v-data["v_los"].v
+ T = data["temperature"].v
+ w = ppv_utils.compute_weight(self.thermal_broad, self.dv_cgs,
+ self.particle_mass, v.flatten(), T.flatten())
+ w[np.isnan(w)] = 0.0
+ return data[self.field]*w.reshape(v.shape)
+ return _intensity
+
+ def transform_spectral_axis(self, rest_value, units):
+ """
+ Change the units of the spectral axis to some equivalent unit, such
+ as energy, wavelength, or frequency, by providing a *rest_value* and the
+ *units* of the new spectral axis. This corresponds to the Doppler-shifting
+ of lines due to gas motions and thermal broadening.
+ """
+ if self.axis_type != "velocity":
+ self.reset_spectral_axis()
+ x0 = self.ds.quan(rest_value, units)
+ if x0.units.dimensions == ytdims.rate or x0.units.dimensions == ytdims.energy:
+ self.vbins = x0*(1.-self.vbins.in_cgs()/clight)
+ elif x0.units.dimensions == ytdims.length:
+ self.vbins = x0/(1.-self.vbins.in_cgs()/clight)
+ self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
+ self.dv = self.vbins[1]-self.vbins[0]
+ dims = self.dv.units.dimensions
+ if dims == ytdims.rate:
+ self.axis_type = "frequency"
+ elif dims == ytdims.length:
+ self.axis_type = "wavelength"
+ elif dims == ytdims.energy:
+ self.axis_type = "energy"
+ elif dims == ytdims.velocity:
+ self.axis_type = "velocity"
+
+ def reset_spectral_axis(self):
+ """
+ Reset the spectral axis to the original velocity range and units.
+ """
+ self.vbins = self._vbins.copy()
+ self.vmid = 0.5*(self.vbins[1:]+self.vbins[:-1])
+ self.dv = self.vbins[1]-self.vbins[0]
+
+ @parallel_root_only
+ def write_fits(self, filename, clobber=True, length_unit=None,
+ sky_scale=None, sky_center=None):
r""" Write the PPVCube to a FITS file.
Parameters
@@ -114,51 +233,71 @@
The name of the file to write.
clobber : boolean
Whether or not to clobber an existing file with the same name.
- length_unit : tuple, optional
- The length that corresponds to the width of the projection in
- (value, unit) form. Accepts a length unit or 'deg'.
+ length_unit : string
+ The units to convert the coordinates to in the file.
+ sky_scale : tuple or YTQuantity
+ Conversion between an angle unit and a length unit, if sky
+ coordinates are desired.
+ Examples: (1.0, "arcsec/kpc"), YTQuantity(0.001, "deg/kpc")
sky_center : tuple, optional
The (RA, Dec) coordinate in degrees of the central pixel if
- *length_unit* is 'deg'.
+ *sky_scale* has been specified.
Examples
--------
- >>> cube.write_fits("my_cube.fits", clobber=False, length_unit=(5,"deg"))
+ >>> cube.write_fits("my_cube.fits", clobber=False, sky_scale=(1.0,"arcsec/kpc"))
"""
- if length_unit[1] == "deg":
- center = sky_center
- types = ["RA---SIN","DEC--SIN"]
+ if sky_scale is None:
+ center = (0.0,0.0)
+ types = ["LINEAR","LINEAR"]
else:
- center = [0.0,0.0]
- types = ["LINEAR","LINEAR"]
+ if iterable(sky_scale):
+ sky_scale = self.ds.quan(sky_scale[0], sky_scale[1])
+ if sky_center is None:
+ center = (30.,45.)
+ else:
+ center = sky_center
+ types = ["RA---TAN","DEC--TAN"]
- v_center = 0.5*(self.v_bnd[0]+self.v_bnd[1]).in_units("m/s").value
+ vunit = fits_info[self.axis_type][0]
+ vtype = fits_info[self.axis_type][1]
- dx = length_unit[0]/self.nx
- dy = length_unit[0]/self.ny
- dv = self.dv.in_units("m/s").value
+ v_center = 0.5*(self.vbins[0]+self.vbins[-1]).in_units(vunit).value
- if length_unit[1] == "deg":
+ if sky_scale:
+ dx = (self.width*sky_scale).in_units("deg").v/self.nx
+ units = "deg"
+ else:
+ if length_unit is None:
+ units = str(self.ds.get_smallest_appropriate_unit(self.width))
+ else:
+ units = length_unit
+ units = sanitize_fits_unit(units)
+ dx = self.width.in_units(units).v/self.nx
+ dy = dx
+ dv = self.dv.in_units(vunit).v
+
+ if sky_scale:
dx *= -1.
w = _astropy.pywcs.WCS(naxis=3)
w.wcs.crpix = [0.5*(self.nx+1), 0.5*(self.ny+1), 0.5*(self.nv+1)]
w.wcs.cdelt = [dx,dy,dv]
- w.wcs.crval = [center[0], center[1], v_center]
- w.wcs.cunit = [length_unit[1],length_unit[1],"m/s"]
- w.wcs.ctype = [types[0],types[1],"VELO-LSR"]
+ w.wcs.crval = [center[0],center[1],v_center]
+ w.wcs.cunit = [units,units,vunit]
+ w.wcs.ctype = [types[0],types[1],vtype]
fib = FITSImageBuffer(self.data.transpose(), fields=self.field, wcs=w)
- fib[0].header["bunit"] = self.field_units
+ fib[0].header["bunit"] = re.sub('()', '', str(self.proj_units))
fib[0].header["btype"] = self.field
fib.writeto(filename, clobber=clobber)
- def _create_intensity(self, i):
- def _intensity(field, data):
- vlos = data["v_los"]
- w = np.abs(vlos-self.vmid[i])/self.dv.in_units(vlos.units)
- w = 1.-w
- w[w < 0.0] = 0.0
- return data[self.field]*w
- return _intensity
+ def __repr__(self):
+ return "PPVCube [%d %d %d] (%s < %s < %s)" % (self.nx, self.ny, self.nv,
+ self.vbins[0],
+ fits_info[self.axis_type][2],
+ self.vbins[-1])
+
+ def __getitem__(self, item):
+ return self.data[item]
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de yt/analysis_modules/ppv_cube/ppv_utils.pyx
--- /dev/null
+++ b/yt/analysis_modules/ppv_cube/ppv_utils.pyx
@@ -0,0 +1,40 @@
+import numpy as np
+cimport numpy as np
+cimport cython
+from yt.utilities.physical_constants import kboltz
+
+cdef extern from "math.h":
+ double exp(double x) nogil
+ double fabs(double x) nogil
+ double sqrt(double x) nogil
+
+cdef double kb = kboltz.v
+cdef double pi = np.pi
+
+ at cython.cdivision(True)
+ at cython.boundscheck(False)
+ at cython.wraparound(False)
+def compute_weight(np.uint8_t thermal_broad,
+ double dv,
+ double m_part,
+ np.ndarray[np.float64_t, ndim=1] v,
+ np.ndarray[np.float64_t, ndim=1] T):
+
+ cdef int i, n
+ cdef double v2_th, x
+ cdef np.ndarray[np.float64_t, ndim=1] w
+
+ n = v.shape[0]
+ w = np.zeros(n)
+
+ for i in range(n):
+ if thermal_broad:
+ if T[i] > 0.0:
+ v2_th = 2.*kb*T[i]/m_part
+ w[i] = dv*exp(-v[i]*v[i]/v2_th)/sqrt(v2_th*pi)
+ else:
+ x = 1.-fabs(v[i])/dv
+ if x > 0.0:
+ w[i] = x
+
+ return w
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de yt/analysis_modules/ppv_cube/setup.py
--- a/yt/analysis_modules/ppv_cube/setup.py
+++ b/yt/analysis_modules/ppv_cube/setup.py
@@ -8,7 +8,10 @@
def configuration(parent_package='', top_path=None):
from numpy.distutils.misc_util import Configuration
config = Configuration('ppv_cube', parent_package, top_path)
- #config.add_subpackage("tests")
+ config.add_extension("ppv_utils",
+ ["yt/analysis_modules/ppv_cube/ppv_utils.pyx"],
+ libraries=["m"])
+ config.add_subpackage("tests")
config.make_config_py() # installs __config__.py
#config.make_svn_version_py()
return config
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de yt/analysis_modules/ppv_cube/tests/test_ppv.py
--- /dev/null
+++ b/yt/analysis_modules/ppv_cube/tests/test_ppv.py
@@ -0,0 +1,62 @@
+"""
+Unit test the sunyaev_zeldovich analysis module.
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2013, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+from yt.frontends.stream.api import load_uniform_grid
+from yt.analysis_modules.ppv_cube.api import PPVCube
+import yt.units as u
+from yt.utilities.physical_constants import kboltz, mh, clight
+import numpy as np
+from yt.testing import *
+
+def setup():
+ """Test specific setup."""
+ from yt.config import ytcfg
+ ytcfg["yt", "__withintesting"] = "True"
+
+def test_ppv():
+
+ np.random.seed(seed=0x4d3d3d3)
+
+ dims = (8,8,1024)
+ v_shift = 1.0e7*u.cm/u.s
+ sigma_v = 2.0e7*u.cm/u.s
+ T_0 = 1.0e8*u.Kelvin
+ data = {"density":(np.ones(dims),"g/cm**3"),
+ "temperature":(T_0.v*np.ones(dims), "K"),
+ "velocity_x":(np.zeros(dims),"cm/s"),
+ "velocity_y":(np.zeros(dims),"cm/s"),
+ "velocity_z":(np.random.normal(loc=v_shift.v,scale=sigma_v.v,size=dims), "cm/s")}
+
+ ds = load_uniform_grid(data, dims)
+
+ cube = PPVCube(ds, "z", "density", dims=dims,
+ velocity_bounds=(-300., 300., "km/s"),
+ thermal_broad=True)
+
+ dv = cube.dv
+ v_th = np.sqrt(2.*kboltz*T_0/(56.*mh) + 2.*sigma_v**2).in_units("km/s")
+ a = cube.data.mean(axis=(0,1)).v
+ b = dv*np.exp(-((cube.vmid+v_shift)/v_th)**2)/(np.sqrt(np.pi)*v_th)
+
+ yield assert_allclose, a, b, 1.0e-2
+
+ E_0 = 6.8*u.keV
+
+ cube.transform_spectral_axis(E_0.v, str(E_0.units))
+
+ dE = -cube.dv
+ delta_E = E_0*v_th.in_cgs()/clight
+ E_shift = E_0*(1.+v_shift/clight)
+
+ c = dE*np.exp(-((cube.vmid-E_shift)/delta_E)**2)/(np.sqrt(np.pi)*delta_E)
+
+ yield assert_allclose, a, c, 1.0e-2
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de yt/analysis_modules/sunyaev_zeldovich/projection.py
--- a/yt/analysis_modules/sunyaev_zeldovich/projection.py
+++ b/yt/analysis_modules/sunyaev_zeldovich/projection.py
@@ -25,6 +25,7 @@
from yt.utilities.parallel_tools.parallel_analysis_interface import \
communication_system, parallel_root_only
from yt import units
+from yt.utilities.on_demand_imports import _astropy
import numpy as np
@@ -114,10 +115,19 @@
----------
axis : integer or string
The axis of the simulation domain along which to make the SZprojection.
- center : array_like or string, optional
- The center of the projection.
- width : float or tuple
- The width of the projection.
+ center : A sequence of floats, a string, or a tuple.
+ The coordinate of the center of the image. If set to 'c', 'center' or
+ left blank, the plot is centered on the middle of the domain. If set to
+ 'max' or 'm', the center will be located at the maximum of the
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
+ as a tuple containing a coordinate and string unit name or by passing
+ in a YTArray. If a list or unitless array is supplied, code units are
+ assumed.
+ width : float, tuple, or YTQuantity.
+ The width of the projection. A float will assume the width is in code units.
+ A (value, unit) tuple or YTQuantity allows for the units of the width to be specified.
nx : integer, optional
The dimensions on a side of the projection image.
source : yt.data_objects.data_containers.YTSelectionContainer, optional
@@ -128,13 +138,8 @@
>>> szprj.on_axis("y", center="max", width=(1.0, "Mpc"), source=my_sphere)
"""
axis = fix_axis(axis, self.ds)
-
- if center == "c":
- ctr = self.ds.domain_center
- elif center == "max":
- v, ctr = self.ds.find_max("density")
- else:
- ctr = center
+ ctr, dctr = self.ds.coordinates.sanitize_center(center, axis)
+ width = self.ds.coordinates.sanitize_width(axis, width, None)
L = np.zeros((3))
L[axis] = 1.0
@@ -143,7 +148,7 @@
self.ds.add_field(("gas","beta_par"), function=beta_par, units="g/cm**3")
setup_sunyaev_zeldovich_fields(self.ds)
proj = self.ds.proj("density", axis, center=ctr, data_source=source)
- frb = proj.to_frb(width, nx)
+ frb = proj.to_frb(width[0], nx, height=width[1])
dens = frb["density"]
Te = frb["t_sz"]/dens
bpar = frb["beta_par"]/dens
@@ -176,10 +181,19 @@
----------
L : array_like
The normal vector of the projection.
- center : array_like or string, optional
- The center of the projection.
- width : float or tuple
- The width of the projection.
+ center : A sequence of floats, a string, or a tuple.
+ The coordinate of the center of the image. If set to 'c', 'center' or
+ left blank, the plot is centered on the middle of the domain. If set to
+ 'max' or 'm', the center will be located at the maximum of the
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
+ as a tuple containing a coordinate and string unit name or by passing
+ in a YTArray. If a list or unitless array is supplied, code units are
+ assumed.
+ width : float, tuple, or YTQuantity.
+ The width of the projection. A float will assume the width is in code units.
+ A (value, unit) tuple or YTQuantity allows for the units of the width to be specified.
nx : integer, optional
The dimensions on a side of the projection image.
source : yt.data_objects.data_containers.YTSelectionContainer, optional
@@ -197,12 +211,7 @@
w = width.in_units("code_length").value
else:
w = width
- if center == "c":
- ctr = self.ds.domain_center
- elif center == "max":
- v, ctr = self.ds.find_max("density")
- else:
- ctr = center
+ ctr, dctr = self.ds.coordinates.sanitize_center(center, L)
if source is not None:
mylog.error("Source argument is not currently supported for off-axis S-Z projections.")
@@ -279,8 +288,7 @@
self.data["TeSZ"] = self.ds.arr(Te, "keV")
@parallel_root_only
- def write_fits(self, filename, units="kpc", sky_center=None, sky_scale=None,
- time_units="Gyr", clobber=True):
+ def write_fits(self, filename, sky_scale=None, sky_center=None, clobber=True):
r""" Export images to a FITS file. Writes the SZ distortion in all
specified frequencies as well as the mass-weighted temperature and the
optical depth. Distance units are in kpc, unless *sky_center*
@@ -290,12 +298,13 @@
----------
filename : string
The name of the FITS file to be written.
- sky_center : tuple of floats, optional
- The center of the observation in (RA, Dec) in degrees. Only used if
- converting to sky coordinates.
- sky_scale : float, optional
- Scale between degrees and kpc. Only used if
- converting to sky coordinates.
+ sky_scale : tuple or YTQuantity
+ Conversion between an angle unit and a length unit, if sky
+ coordinates are desired.
+ Examples: (1.0, "arcsec/kpc"), YTQuantity(0.001, "deg/kpc")
+ sky_center : tuple, optional
+ The (RA, Dec) coordinate in degrees of the central pixel if
+ *sky_scale* has been specified.
clobber : boolean, optional
If the file already exists, do we overwrite?
@@ -304,27 +313,43 @@
>>> # This example just writes out a FITS file with kpc coords
>>> szprj.write_fits("SZbullet.fits", clobber=False)
>>> # This example uses sky coords
- >>> sky_scale = 1./3600. # One arcsec per kpc
+ >>> sky_scale = (1., "arcsec/kpc") # One arcsec per kpc
>>> sky_center = (30., 45.) # In degrees
>>> szprj.write_fits("SZbullet.fits", sky_center=sky_center, sky_scale=sky_scale)
"""
+ from yt.utilities.fits_image import FITSImageBuffer, sanitize_fits_unit
- deltas = np.array([self.dx.in_units(units),
- self.dy.in_units(units)])
+ if sky_scale is None:
+ center = (0.0,0.0)
+ types = ["LINEAR","LINEAR"]
+ else:
+ if iterable(sky_scale):
+ sky_scale = self.ds.quan(sky_scale[0], sky_scale[1])
+ if sky_center is None:
+ center = (30.,45.)
+ else:
+ center = sky_center
+ types = ["RA---TAN","DEC--TAN"]
- if sky_center is None:
- center = [0.0]*2
- else:
- center = sky_center
+ units = self.ds.get_smallest_appropriate_unit(self.width)
+ units = sanitize_fits_unit(units)
+ # Hack because FITS is stupid and doesn't understand case
+ dx = self.dx.in_units(units)
+ if sky_scale:
+ dx = (dx*sky_scale).in_units("deg")
units = "deg"
- deltas *= sky_scale
- deltas[0] *= -1.
+ dy = dx
+ if sky_scale:
+ dx *= -1.
- from yt.utilities.fits_image import FITSImageBuffer
- fib = FITSImageBuffer(self.data, fields=self.data.keys(),
- center=center, units=units,
- scale=deltas)
- fib.update_all_headers("Time", float(self.ds.current_time.in_units(time_units).value))
+ w = _astropy.pywcs.WCS(naxis=2)
+ w.wcs.crpix = [0.5*(self.nx+1)]*2
+ w.wcs.cdelt = [dx.v,dy.v]
+ w.wcs.crval = center
+ w.wcs.cunit = [units]*2
+ w.wcs.ctype = types
+
+ fib = FITSImageBuffer(self.data, fields=self.data.keys(), wcs=w)
fib.writeto(filename, clobber=clobber)
@parallel_root_only
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de yt/data_objects/construction_data_containers.py
--- a/yt/data_objects/construction_data_containers.py
+++ b/yt/data_objects/construction_data_containers.py
@@ -191,6 +191,8 @@
"integrate" : integration along the axis
"mip" : maximum intensity projection
"sum" : same as "integrate", except that we don't multiply by the path length
+ WARNING: The "sum" option should only be used for uniform resolution grid
+ datasets, as other datasets may result in unphysical images.
style : string, optional
The same as the method keyword. Deprecated as of version 3.0.2.
Please use method keyword instead.
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de yt/frontends/fits/data_structures.py
--- a/yt/frontends/fits/data_structures.py
+++ b/yt/frontends/fits/data_structures.py
@@ -42,11 +42,11 @@
unit_prefixes
from yt.units import dimensions
from yt.units.yt_array import YTQuantity
-from yt.utilities.on_demand_imports import _astropy
+from yt.utilities.on_demand_imports import _astropy, NotAModule
-lon_prefixes = ["X","RA","GLON"]
-lat_prefixes = ["Y","DEC","GLAT"]
-delimiters = ["*", "/", "-", "^"]
+lon_prefixes = ["X","RA","GLON","LINEAR"]
+lat_prefixes = ["Y","DEC","GLAT","LINEAR"]
+delimiters = ["*", "/", "-", "^", "(", ")"]
delimiters += [str(i) for i in xrange(10)]
regex_pattern = '|'.join(re.escape(_) for _ in delimiters)
@@ -552,7 +552,7 @@
x = 0
for p in lon_prefixes+lat_prefixes+spec_names.keys():
y = np_char.startswith(self.axis_names[:self.dimensionality], p)
- x += y.sum()
+ x += np.any(y)
if x == self.dimensionality: self._setup_spec_cube()
def _setup_spec_cube(self):
@@ -582,6 +582,12 @@
self.lon_axis = np.where(self.lon_axis)[0][0]
self.lon_name = ctypes[self.lon_axis].split("-")[0].lower()
+ if self.lat_axis == self.lon_axis and self.lat_name == self.lon_name:
+ self.lat_axis = 1
+ self.lon_axis = 0
+ self.lat_name = "Y"
+ self.lon_name = "X"
+
if self.wcs.naxis > 2:
self.spec_axis = np.zeros((end-1), dtype="bool")
@@ -658,6 +664,8 @@
ext = args[0].rsplit(".", 1)[0].rsplit(".", 1)[-1]
if ext.upper() not in ("FITS", "FTS"):
return False
+ elif isinstance(_astropy.pyfits, NotAModule):
+ raise RuntimeError("This appears to be a FITS file, but AstroPy is not installed.")
try:
with warnings.catch_warnings():
warnings.filterwarnings('ignore', category=UserWarning, append=True)
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de yt/geometry/coordinates/coordinate_handler.py
--- a/yt/geometry/coordinates/coordinate_handler.py
+++ b/yt/geometry/coordinates/coordinate_handler.py
@@ -183,7 +183,15 @@
elif isinstance(center, YTArray):
return self.ds.arr(center), self.convert_to_cartesian(center)
elif iterable(center):
- if iterable(center[0]) and isinstance(center[1], basestring):
+ if isinstance(center[0], basestring) and isinstance(center[1], basestring):
+ if center[0].lower() == "min":
+ v, center = self.ds.find_min(center[1])
+ elif center[0].lower() == "max":
+ v, center = self.ds.find_max(center[1])
+ else:
+ raise RuntimeError("center keyword \"%s\" not recognized" % center)
+ center = self.ds.arr(center, 'code_length')
+ elif iterable(center[0]) and isinstance(center[1], basestring):
center = self.ds.arr(center[0], center[1])
else:
center = self.ds.arr(center, 'code_length')
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de yt/geometry/coordinates/spec_cube_coordinates.py
--- a/yt/geometry/coordinates/spec_cube_coordinates.py
+++ b/yt/geometry/coordinates/spec_cube_coordinates.py
@@ -26,8 +26,19 @@
self.axis_name = {}
self.axis_id = {}
- for axis, axis_name in zip([ds.lon_axis, ds.lat_axis, ds.spec_axis],
- ["Image\ x", "Image\ y", ds.spec_name]):
+ self.default_unit_label = {}
+ if ds.lon_name == "X" and ds.lat_name == "Y":
+ names = ["x","y"]
+ else:
+ names = ["Image\ x", "Image\ y"]
+ self.default_unit_label[ds.lon_axis] = "pixel"
+ self.default_unit_label[ds.lat_axis] = "pixel"
+ names.append(ds.spec_name)
+ axes = [ds.lon_axis, ds.lat_axis, ds.spec_axis]
+ self.default_unit_label[ds.spec_axis] = ds.spec_unit
+
+ for axis, axis_name in zip(axes, names):
+
lower_ax = "xyz"[axis]
upper_ax = lower_ax.upper()
@@ -40,11 +51,6 @@
self.axis_id[axis] = axis
self.axis_id[axis_name] = axis
- self.default_unit_label = {}
- self.default_unit_label[ds.lon_axis] = "pixel"
- self.default_unit_label[ds.lat_axis] = "pixel"
- self.default_unit_label[ds.spec_axis] = ds.spec_unit
-
def _spec_axis(ax, x, y):
p = (x,y)[ax]
return [self.ds.pixel2spec(pp).v for pp in p]
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de yt/units/unit_lookup_table.py
--- a/yt/units/unit_lookup_table.py
+++ b/yt/units/unit_lookup_table.py
@@ -172,6 +172,10 @@
'y': 1e-24, # yocto
}
+latex_prefixes = {
+ "u" : "\\mu",
+ }
+
prefixable_units = (
"m",
"pc",
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de yt/units/unit_object.py
--- a/yt/units/unit_object.py
+++ b/yt/units/unit_object.py
@@ -26,7 +26,8 @@
from yt.units.unit_lookup_table import \
latex_symbol_lut, unit_prefixes, \
prefixable_units, cgs_base_units, \
- mks_base_units, cgs_conversions
+ mks_base_units, latex_prefixes, \
+ cgs_conversions
from yt.units.unit_registry import UnitRegistry
import copy
@@ -544,9 +545,14 @@
prefix_value = unit_prefixes[possible_prefix]
if symbol_str not in latex_symbol_lut:
+ if possible_prefix in latex_prefixes:
+ sstr = symbol_str.replace(possible_prefix,
+ '{'+latex_prefixes[possible_prefix]+'}')
+ else:
+ sstr = symbol_str
latex_symbol_lut[symbol_str] = \
latex_symbol_lut[symbol_wo_prefix].replace(
- '{'+symbol_wo_prefix+'}', '{'+symbol_str+'}')
+ '{'+symbol_wo_prefix+'}', '{'+sstr+'}')
# don't forget to account for the prefix value!
return (unit_data[0] * prefix_value, unit_data[1])
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de yt/utilities/fits_image.py
--- a/yt/utilities/fits_image.py
+++ b/yt/utilities/fits_image.py
@@ -15,7 +15,8 @@
from yt.visualization.fixed_resolution import FixedResolutionBuffer
from yt.data_objects.construction_data_containers import YTCoveringGridBase
from yt.utilities.on_demand_imports import _astropy
-from yt.units.yt_array import YTQuantity
+from yt.units.yt_array import YTQuantity, YTArray
+import re
pyfits = _astropy.pyfits
pywcs = _astropy.pywcs
@@ -27,8 +28,7 @@
class FITSImageBuffer(HDUList):
- def __init__(self, data, fields=None, units="cm",
- center=None, scale=None, wcs=None):
+ def __init__(self, data, fields=None, units="cm", wcs=None):
r""" Initialize a FITSImageBuffer object.
FITSImageBuffer contains a list of FITS ImageHDU instances, and
@@ -49,29 +49,32 @@
keys, it will use these for the fields. If *data* is just a
single array one field name must be specified.
units : string
- The units of the WCS coordinates, default "cm".
- center : array_like, optional
- The coordinates [xctr,yctr] of the images in units
- *units*. If *units* is not specified, defaults to the origin.
- scale : tuple of floats, optional
- Pixel scale in unit *units*. Will be ignored if *data* is
- a FixedResolutionBuffer or a YTCoveringGrid. Must be
- specified otherwise, or if *units* is "deg".
+ The units of the WCS coordinates. Only applies
+ to FixedResolutionBuffers or YTCoveringGrids. Defaults to "cm".
wcs : `astropy.wcs.WCS` instance, optional
- Supply an AstroPy WCS instance to override automatic WCS creation.
+ Supply an AstroPy WCS instance. Will override automatic WCS
+ creation from FixedResolutionBuffers and YTCoveringGrids.
Examples
--------
+ >>> # This example uses a FRB.
>>> ds = load("sloshing_nomag2_hdf5_plt_cnt_0150")
>>> prj = ds.proj(2, "kT", weight_field="density")
>>> frb = prj.to_frb((0.5, "Mpc"), 800)
>>> # This example just uses the FRB and puts the coords in kpc.
>>> f_kpc = FITSImageBuffer(frb, fields="kT", units="kpc")
- >>> # This example specifies sky coordinates.
- >>> scale = [1./3600.]*2 # One arcsec per pixel
- >>> f_deg = FITSImageBuffer(frb, fields="kT", units="deg",
- scale=scale, center=(30., 45.))
+ >>> # This example specifies a specific WCS.
+ >>> from astropy.wcs import WCS
+ >>> w = WCS(naxis=self.dimensionality)
+ >>> w.wcs.crval = [30., 45.] # RA, Dec in degrees
+ >>> w.wcs.cunit = ["deg"]*2
+ >>> nx, ny = 800, 800
+ >>> w.wcs.crpix = [0.5*(nx+1), 0.5*(ny+1)]
+ >>> w.wcs.ctype = ["RA---TAN","DEC--TAN"]
+ >>> scale = 1./3600. # One arcsec per pixel
+ >>> w.wcs.cdelt = [-scale, scale]
+ >>> f_deg = FITSImageBuffer(frb, fields="kT", wcs=w)
>>> f_deg.writeto("temp.fits")
"""
@@ -98,8 +101,14 @@
first = True
+ self.field_units = {}
+
for key in fields:
if key not in exclude_fields:
+ if hasattr(img_data[key], "units"):
+ self.field_units[key] = str(img_data[key].units)
+ else:
+ self.field_units[key] = "dimensionless"
mylog.info("Making a FITS image of field %s" % (key))
if first:
hdu = pyfits.PrimaryHDU(np.array(img_data[key]))
@@ -109,7 +118,7 @@
hdu.name = key
hdu.header["btype"] = key
if hasattr(img_data[key], "units"):
- hdu.header["bunit"] = str(img_data[key].units)
+ hdu.header["bunit"] = re.sub('()', '', str(img_data[key].units))
self.append(hdu)
self.dimensionality = len(self[0].data.shape)
@@ -121,25 +130,11 @@
has_coords = (isinstance(img_data, FixedResolutionBuffer) or
isinstance(img_data, YTCoveringGridBase))
-
- if center is None:
- if units == "deg":
- mylog.error("Please specify center=(RA, Dec) in degrees.")
- raise ValueError
- elif not has_coords:
- mylog.warning("Setting center to the origin.")
- center = [0.0]*self.dimensionality
-
- if scale is None:
- if units == "deg" or not has_coords and wcs is None:
- mylog.error("Please specify scale=(dx,dy[,dz]) in %s." % (units))
- raise ValueError
if wcs is None:
w = pywcs.WCS(header=self[0].header, naxis=self.dimensionality)
w.wcs.crpix = 0.5*(np.array(self.shape)+1)
- proj_type = ["linear"]*self.dimensionality
- if isinstance(img_data, FixedResolutionBuffer) and units != "deg":
+ if isinstance(img_data, FixedResolutionBuffer):
# FRBs are a special case where we have coordinate
# information, so we take advantage of this and
# construct the WCS object
@@ -151,28 +146,20 @@
elif isinstance(img_data, YTCoveringGridBase):
dx, dy, dz = img_data.dds.in_units(units)
center = 0.5*(img_data.left_edge+img_data.right_edge).in_units(units)
- elif units == "deg" and self.dimensionality == 2:
- dx = -scale[0]
- dy = scale[1]
- proj_type = ["RA---TAN","DEC--TAN"]
else:
- dx = scale[0]
- dy = scale[1]
- if self.dimensionality == 3: dz = scale[2]
-
+ # We default to pixel coordinates if nothing is provided
+ dx, dy, dz = 1.0, 1.0, 1.0
+ center = 0.5*(np.array(self.shape)+1)
w.wcs.crval = center
- w.wcs.cunit = [units]*self.dimensionality
- w.wcs.ctype = proj_type
-
+ if has_coords:
+ w.wcs.cunit = [units]*self.dimensionality
if self.dimensionality == 2:
w.wcs.cdelt = [dx,dy]
elif self.dimensionality == 3:
w.wcs.cdelt = [dx,dy,dz]
-
+ w.wcs.ctype = ["linear"]*self.dimensionality
self._set_wcs(w)
-
else:
-
self._set_wcs(wcs)
def _set_wcs(self, wcs):
@@ -194,7 +181,7 @@
for img in self: img.header[key] = value
def keys(self):
- return [f.name for f in self]
+ return [f.name.lower() for f in self]
def has_key(self, key):
return key in self.keys()
@@ -249,33 +236,71 @@
import aplpy
return aplpy.FITSFigure(self, **kwargs)
+ def get_data(self, field):
+ return YTArray(self[field].data, self.field_units[field])
+
+ def set_unit(self, field, units):
+ """
+ Set the units of *field* to *units*.
+ """
+ new_data = YTArray(self[field].data, self.field_units[field]).in_units(units)
+ self[field].data = new_data.v
+ self[field].header["bunit"] = units
+ self.field_units[field] = units
+
axis_wcs = [[1,2],[0,2],[0,1]]
-def construct_image(data_source, center=None):
+def sanitize_fits_unit(unit):
+ if unit == "Mpc":
+ mylog.info("Changing FITS file unit to kpc.")
+ unit = "kpc"
+ elif unit == "au":
+ unit = "AU"
+ return unit
+
+def construct_image(data_source, center=None, width=None, image_res=None):
ds = data_source.ds
axis = data_source.axis
+ if center is None or width is None:
+ center = ds.domain_center[axis_wcs[axis]]
+ if width is None:
+ width = ds.domain_width[axis_wcs[axis]]
+ mylog.info("Making an image of the entire domain, "+
+ "so setting the center to the domain center.")
+ else:
+ width = ds.coordinates.sanitize_width(axis, width, None)
+ if image_res is None:
+ dd = ds.all_data()
+ dx, dy = [dd.quantities.extrema("d%s" % "xyz"[idx])[0]
+ for idx in axis_wcs[axis]]
+ nx = int((width[0]/dx).in_units("dimensionless"))
+ ny = int((width[1]/dy).in_units("dimensionless"))
+ else:
+ if iterable(image_res):
+ nx, ny = image_res
+ else:
+ nx, ny = image_res, image_res
+ dx, dy = width[0]/nx, width[1]/ny
+ crpix = [0.5*(nx+1), 0.5*(ny+1)]
if hasattr(ds, "wcs"):
- # This is a FITS dataset
- nx, ny = ds.domain_dimensions[axis_wcs[axis]]
- crpix = [ds.wcs.wcs.crpix[idx] for idx in axis_wcs[axis]]
- cdelt = [ds.wcs.wcs.cdelt[idx] for idx in axis_wcs[axis]]
- crval = [ds.wcs.wcs.crval[idx] for idx in axis_wcs[axis]]
+ # This is a FITS dataset, so we use it to construct the WCS
cunit = [str(ds.wcs.wcs.cunit[idx]) for idx in axis_wcs[axis]]
ctype = [ds.wcs.wcs.ctype[idx] for idx in axis_wcs[axis]]
+ cdelt = [ds.wcs.wcs.cdelt[idx] for idx in axis_wcs[axis]]
+ ctr_pix = center.in_units("code_length")[:self.dimensionality].v
+ crval = ds.wcs.wcs_pix2world(ctr_pix.reshape(1,self.dimensionality))[0]
+ crval = [crval[idx] for idx in axis_wcs[axis]]
else:
- # This is some other kind of dataset
- unit = ds.get_smallest_appropriate_unit(ds.domain_width.max())
- if center is None:
- crval = [0.0,0.0]
- else:
- crval = [(ds.domain_center-center)[idx].in_units(unit) for idx in axis_wcs[axis]]
- dx = ds.index.get_smallest_dx()
- nx, ny = (ds.domain_width[axis_wcs[axis]]/dx).ndarray_view().astype("int")
- crpix = [0.5*(nx+1), 0.5*(ny+1)]
- cdelt = [dx.in_units(unit)]*2
+ # This is some other kind of dataset
+ unit = str(width[0].units)
+ if unit == "unitary":
+ unit = ds.get_smallest_appropriate_unit(ds.domain_width.max())
+ unit = sanitize_fits_unit(unit)
cunit = [unit]*2
ctype = ["LINEAR"]*2
- frb = data_source.to_frb((1.0,"unitary"), (nx,ny))
+ cdelt = [dx.in_units(unit)]*2
+ crval = [center[idx].in_units(unit) for idx in axis_wcs[axis]]
+ frb = data_source.to_frb(width[0], (nx,ny), center=center, height=width[1])
w = pywcs.WCS(naxis=2)
w.wcs.crpix = crpix
w.wcs.cdelt = cdelt
@@ -296,21 +321,47 @@
The axis of the slice. One of "x","y","z", or 0,1,2.
fields : string or list of strings
The fields to slice
- center : A sequence floats, a string, or a tuple.
- The coordinate of the origin of the image. If set to 'c', 'center' or
+ center : A sequence of floats, a string, or a tuple.
+ The coordinate of the center of the image. If set to 'c', 'center' or
left blank, the plot is centered on the middle of the domain. If set to
'max' or 'm', the center will be located at the maximum of the
- ('gas', 'density') field. Units can be specified by passing in center
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
as a tuple containing a coordinate and string unit name or by passing
- in a YTArray. If a list or unitless array is supplied, code units are
+ in a YTArray. If a list or unitless array is supplied, code units are
assumed.
+ width : tuple or a float.
+ Width can have four different formats to support windows with variable
+ x and y widths. They are:
+
+ ================================== =======================
+ format example
+ ================================== =======================
+ (float, string) (10,'kpc')
+ ((float, string), (float, string)) ((10,'kpc'),(15,'kpc'))
+ float 0.2
+ (float, float) (0.2, 0.3)
+ ================================== =======================
+
+ For example, (10, 'kpc') requests a plot window that is 10 kiloparsecs
+ wide in the x and y directions, ((10,'kpc'),(15,'kpc')) requests a
+ window that is 10 kiloparsecs wide along the x axis and 15
+ kiloparsecs wide along the y axis. In the other two examples, code
+ units are assumed, for example (0.2, 0.3) requests a plot that has an
+ x width of 0.2 and a y width of 0.3 in code units. If units are
+ provided the resulting plot axis labels will use the supplied units.
+ image_res : an int or 2-tuple of ints
+ Specify the resolution of the resulting image. If not provided, it will be
+ determined based on the minimum cell size of the dataset.
"""
- def __init__(self, ds, axis, fields, center="c", **kwargs):
+ def __init__(self, ds, axis, fields, center="c", width=None, **kwargs):
fields = ensure_list(fields)
axis = fix_axis(axis, ds)
- center = ds.coordinates.sanitize_center(center, axis)
+ center, dcenter = ds.coordinates.sanitize_center(center, axis)
slc = ds.slice(axis, center[axis], **kwargs)
- w, frb = construct_image(slc, center=center)
+ w, frb = construct_image(slc, center=dcenter, width=width,
+ image_res=image_res)
super(FITSSlice, self).__init__(frb, fields=fields, wcs=w)
for i, field in enumerate(fields):
self[i].header["bunit"] = str(frb[field].units)
@@ -329,21 +380,48 @@
The fields to project
weight_field : string
The field used to weight the projection.
- center : A sequence floats, a string, or a tuple.
- The coordinate of the origin of the image. If set to 'c', 'center' or
- left blank, the plot is centered on the middle of the domain. If set to
- 'max' or 'm', the center will be located at the maximum of the
- ('gas', 'density') field. Units can be specified by passing in center
- as a tuple containing a coordinate and string unit name or by passing
- in a YTArray. If a list or unitless array is supplied, code units are
- assumed.
+ center : A sequence of floats, a string, or a tuple.
+ The coordinate of the center of the image. If set to 'c', 'center' or
+ left blank, the plot is centered on the middle of the domain. If set to
+ 'max' or 'm', the center will be located at the maximum of the
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
+ as a tuple containing a coordinate and string unit name or by passing
+ in a YTArray. If a list or unitless array is supplied, code units are
+ assumed.
+ width : tuple or a float.
+ Width can have four different formats to support windows with variable
+ x and y widths. They are:
+
+ ================================== =======================
+ format example
+ ================================== =======================
+ (float, string) (10,'kpc')
+ ((float, string), (float, string)) ((10,'kpc'),(15,'kpc'))
+ float 0.2
+ (float, float) (0.2, 0.3)
+ ================================== =======================
+
+ For example, (10, 'kpc') requests a plot window that is 10 kiloparsecs
+ wide in the x and y directions, ((10,'kpc'),(15,'kpc')) requests a
+ window that is 10 kiloparsecs wide along the x axis and 15
+ kiloparsecs wide along the y axis. In the other two examples, code
+ units are assumed, for example (0.2, 0.3) requests a plot that has an
+ x width of 0.2 and a y width of 0.3 in code units. If units are
+ provided the resulting plot axis labels will use the supplied units.
+ image_res : an int or 2-tuple of ints
+ Specify the resolution of the resulting image. If not provided, it will be
+ determined based on the minimum cell size of the dataset.
"""
- def __init__(self, ds, axis, fields, center="c", weight_field=None, **kwargs):
+ def __init__(self, ds, axis, fields, center="c", width=None,
+ weight_field=None, image_res=None, **kwargs):
fields = ensure_list(fields)
axis = fix_axis(axis, ds)
- center = ds.coordinates.sanitize_center(center, axis)
+ center, dcenter = ds.coordinates.sanitize_center(center, axis)
prj = ds.proj(fields[0], axis, weight_field=weight_field, **kwargs)
- w, frb = construct_image(prj, center=center)
+ w, frb = construct_image(prj, center=dcenter, width=width,
+ image_res=image_res)
super(FITSProjection, self).__init__(frb, fields=fields, wcs=w)
for i, field in enumerate(fields):
self[i].header["bunit"] = str(frb[field].units)
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de yt/utilities/on_demand_imports.py
--- a/yt/utilities/on_demand_imports.py
+++ b/yt/utilities/on_demand_imports.py
@@ -132,4 +132,15 @@
self._interpolate = interpolate
return self._interpolate
+ _special = None
+ @property
+ def special(self):
+ if self._special is None:
+ try:
+ import scipy.special as special
+ except ImportError:
+ special = NotAModule(self._name)
+ self._special = special
+ return self._special
+
_scipy = scipy_imports()
\ No newline at end of file
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de yt/visualization/fixed_resolution.py
--- a/yt/visualization/fixed_resolution.py
+++ b/yt/visualization/fixed_resolution.py
@@ -418,9 +418,9 @@
width, dd.resolution, item,
weight=dd.weight_field, volume=dd.volume,
no_ghost=dd.no_ghost, interpolated=dd.interpolated,
- north_vector=dd.north_vector)
+ north_vector=dd.north_vector, method=dd.method)
units = Unit(dd.ds.field_info[item].units, registry=dd.ds.unit_registry)
- if dd.weight_field is None:
+ if dd.weight_field is None and dd.method == "integrate":
units *= Unit('cm', registry=dd.ds.unit_registry)
ia = ImageArray(buff.swapaxes(0,1), input_units=units, info=self._get_info(item))
self[item] = ia
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -50,6 +50,8 @@
Unit
from yt.units.unit_registry import \
UnitParseError
+from yt.units.unit_lookup_table import \
+ prefixable_units, latex_prefixes
from yt.units.yt_array import \
YTArray, YTQuantity
from yt.utilities.png_writer import \
@@ -771,9 +773,10 @@
if hasattr(self.ds.coordinates, "default_unit_label"):
axax = getattr(self.ds.coordinates,
"%s_axis" % ("xy"[i]))[axis_index]
- un = self.ds.coordinates.default_unit_label[axax]
- axes_unit_labels[i] = '\/\/\left('+un+'\right)'
- continue
+ unn = self.ds.coordinates.default_unit_label.get(axax, "")
+ if unn != "":
+ axes_unit_labels[i] = '\/\/\left('+unn+'\right)'
+ continue
# Use sympy to factor h out of the unit. In this context 'un'
# is a string, so we call the Unit constructor.
expr = Unit(un, registry=self.ds.unit_registry).expr
@@ -794,6 +797,11 @@
raise RuntimeError
if un in formatted_length_unit_names:
un = formatted_length_unit_names[un]
+ pp = un[0]
+ if pp in latex_prefixes:
+ symbol_wo_prefix = un[1:]
+ if symbol_wo_prefix in prefixable_units:
+ un = un.replace(pp, "{"+latex_prefixes[pp]+"}", 1)
if un not in ['1', 'u', 'unitary']:
if hinv:
un = un + '\,h^{-1}'
@@ -940,13 +948,15 @@
or the axis name itself
fields : string
The name of the field(s) to be plotted.
- center : A sequence floats, a string, or a tuple.
+ center : A sequence of floats, a string, or a tuple.
The coordinate of the center of the image. If set to 'c', 'center' or
left blank, the plot is centered on the middle of the domain. If set to
'max' or 'm', the center will be located at the maximum of the
- ('gas', 'density') field. Units can be specified by passing in center
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
as a tuple containing a coordinate and string unit name or by passing
- in a YTArray. If a list or unitless array is supplied, code units are
+ in a YTArray. If a list or unitless array is supplied, code units are
assumed.
width : tuple or a float.
Width can have four different formats to support windows with variable
@@ -1061,13 +1071,15 @@
or the axis name itself
fields : string
The name of the field(s) to be plotted.
- center : A sequence floats, a string, or a tuple.
+ center : A sequence of floats, a string, or a tuple.
The coordinate of the center of the image. If set to 'c', 'center' or
left blank, the plot is centered on the middle of the domain. If set to
'max' or 'm', the center will be located at the maximum of the
- ('gas', 'density') field. Units can be specified by passing in center
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
as a tuple containing a coordinate and string unit name or by passing
- in a YTArray. If a list or unitless array is supplied, code units are
+ in a YTArray. If a list or unitless array is supplied, code units are
assumed.
width : tuple or a float.
Width can have four different formats to support windows with variable
@@ -1144,7 +1156,9 @@
"sum" : This method is the same as integrate, except that it does not
multiply by a path length when performing the integration, and is
- just a straight summation of the field along the given axis.
+ just a straight summation of the field along the given axis. WARNING:
+ This should only be used for uniform resolution grid datasets, as other
+ datasets may result in unphysical images.
proj_style : string
The method of projection--same as method keyword. Deprecated as of
version 3.0.2. Please use method instead.
@@ -1218,13 +1232,15 @@
The vector normal to the slicing plane.
fields : string
The name of the field(s) to be plotted.
- center : A sequence floats, a string, or a tuple.
+ center : A sequence of floats, a string, or a tuple.
The coordinate of the center of the image. If set to 'c', 'center' or
left blank, the plot is centered on the middle of the domain. If set to
'max' or 'm', the center will be located at the maximum of the
- ('gas', 'density') field. Units can be specified by passing in center
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
as a tuple containing a coordinate and string unit name or by passing
- in a YTArray. If a list or unitless array is supplied, code units are
+ in a YTArray. If a list or unitless array is supplied, code units are
assumed.
width : tuple or a float.
Width can have four different formats to support windows with variable
@@ -1284,12 +1300,11 @@
class OffAxisProjectionDummyDataSource(object):
_type_name = 'proj'
- method = 'integrate'
_key_fields = []
def __init__(self, center, ds, normal_vector, width, fields,
interpolated, resolution = (800,800), weight=None,
volume=None, no_ghost=False, le=None, re=None,
- north_vector=None):
+ north_vector=None, method="integrate"):
self.center = center
self.ds = ds
self.axis = 4 # always true for oblique data objects
@@ -1306,6 +1321,7 @@
self.le = le
self.re = re
self.north_vector = north_vector
+ self.method = method
def _determine_fields(self, *args):
return self.dd._determine_fields(*args)
@@ -1329,13 +1345,15 @@
The vector normal to the slicing plane.
fields : string
The name of the field(s) to be plotted.
- center : A sequence floats, a string, or a tuple.
+ center : A sequence of floats, a string, or a tuple.
The coordinate of the center of the image. If set to 'c', 'center' or
left blank, the plot is centered on the middle of the domain. If set to
'max' or 'm', the center will be located at the maximum of the
- ('gas', 'density') field. Units can be specified by passing in center
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
as a tuple containing a coordinate and string unit name or by passing
- in a YTArray. If a list or unitless array is supplied, code units are
+ in a YTArray. If a list or unitless array is supplied, code units are
assumed.
width : tuple or a float.
Width can have four different formats to support windows with variable
@@ -1376,7 +1394,20 @@
set, an arbitrary grid-aligned north-vector is chosen.
fontsize : integer
The size of the fonts for the axis, colorbar, and tick labels.
+ method : string
+ The method of projection. Valid methods are:
+ "integrate" with no weight_field specified : integrate the requested
+ field along the line of sight.
+
+ "integrate" with a weight_field specified : weight the requested
+ field by the weighting field and integrate along the line of sight.
+
+ "sum" : This method is the same as integrate, except that it does not
+ multiply by a path length when performing the integration, and is
+ just a straight summation of the field along the given axis. WARNING:
+ This should only be used for uniform resolution grid datasets, as other
+ datasets may result in unphysical images.
"""
_plot_type = 'OffAxisProjection'
_frb_generator = OffAxisProjectionFixedResolutionBuffer
@@ -1384,7 +1415,7 @@
def __init__(self, ds, normal, fields, center='c', width=None,
depth=(1, '1'), axes_unit=None, weight_field=None,
max_level=None, north_vector=None, volume=None, no_ghost=False,
- le=None, re=None, interpolated=False, fontsize=18):
+ le=None, re=None, interpolated=False, fontsize=18, method="integrate"):
(bounds, center_rot) = \
get_oblique_window_parameters(normal,center,width,ds,depth=depth)
fields = ensure_list(fields)[:]
@@ -1394,7 +1425,7 @@
OffAxisProj = OffAxisProjectionDummyDataSource(
center_rot, ds, normal, oap_width, fields, interpolated,
weight=weight_field, volume=volume, no_ghost=no_ghost,
- le=le, re=re, north_vector=north_vector)
+ le=le, re=re, north_vector=north_vector, method=method)
# If a non-weighted, integral projection, assure field-label reflects that
if weight_field is None and OffAxisProj.method == "integrate":
self.projected = True
@@ -1736,9 +1767,11 @@
The coordinate of the center of the image. If set to 'c', 'center' or
left blank, the plot is centered on the middle of the domain. If set to
'max' or 'm', the center will be located at the maximum of the
- ('gas', 'density') field. Units can be specified by passing in center
+ ('gas', 'density') field. Centering on the max or min of a specific
+ field is supported by providing a tuple such as ("min","temperature") or
+ ("max","dark_matter_density"). Units can be specified by passing in *center*
as a tuple containing a coordinate and string unit name or by passing
- in a YTArray. If a list or unitless array is supplied, code units are
+ in a YTArray. If a list or unitless array is supplied, code units are
assumed.
width : tuple or a float.
Width can have four different formats to support windows with variable
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de yt/visualization/volume_rendering/camera.py
--- a/yt/visualization/volume_rendering/camera.py
+++ b/yt/visualization/volume_rendering/camera.py
@@ -2171,7 +2171,8 @@
class ProjectionCamera(Camera):
def __init__(self, center, normal_vector, width, resolution,
field, weight=None, volume=None, no_ghost = False,
- north_vector=None, ds=None, interpolated=False):
+ north_vector=None, ds=None, interpolated=False,
+ method="integrate"):
if not interpolated:
volume = 1
@@ -2180,6 +2181,7 @@
self.field = field
self.weight = weight
self.resolution = resolution
+ self.method = method
fields = [field]
if self.weight is not None:
@@ -2250,13 +2252,14 @@
dd = ds.all_data()
field = dd._determine_fields([self.field])[0]
finfo = ds._get_field_info(*field)
- if self.weight is None:
- dl = self.width[2]
- image *= dl
- else:
- image[:,:,0] /= image[:,:,1]
+ dl = 1.0
+ if self.method == "integrate":
+ if self.weight is None:
+ dl = self.width[2].in_units("cm")
+ else:
+ image[:,:,0] /= image[:,:,1]
- return image[:,:,0]
+ return ImageArray(image[:,:,0], finfo.units)*dl
def _render(self, double_check, num_threads, image, sampler):
@@ -2342,7 +2345,7 @@
def off_axis_projection(ds, center, normal_vector, width, resolution,
field, weight = None,
volume = None, no_ghost = False, interpolated = False,
- north_vector = None):
+ north_vector = None, method = "integrate"):
r"""Project through a dataset, off-axis, and return the image plane.
This function will accept the necessary items to integrate through a volume
@@ -2386,6 +2389,20 @@
If True, the data is first interpolated to vertex-centered data,
then tri-linearly interpolated along the ray. Not suggested for
quantitative studies.
+ method : string
+ The method of projection. Valid methods are:
+
+ "integrate" with no weight_field specified : integrate the requested
+ field along the line of sight.
+
+ "integrate" with a weight_field specified : weight the requested
+ field by the weighting field and integrate along the line of sight.
+
+ "sum" : This method is the same as integrate, except that it does not
+ multiply by a path length when performing the integration, and is
+ just a straight summation of the field along the given axis. WARNING:
+ This should only be used for uniform resolution grid datasets, as other
+ datasets may result in unphysical images.
Returns
-------
@@ -2403,7 +2420,7 @@
projcam = ProjectionCamera(center, normal_vector, width, resolution,
field, weight=weight, ds=ds, volume=volume,
no_ghost=no_ghost, interpolated=interpolated,
- north_vector=north_vector)
+ north_vector=north_vector, method=method)
image = projcam.snapshot()
return image[:,:]
diff -r ab0c5ed83cc0eff0c8f72e3faca3cecfe9cc93e6 -r e433a9791f5dadcd5de80c5d9f801526335455de yt/visualization/volume_rendering/image_handling.py
--- a/yt/visualization/volume_rendering/image_handling.py
+++ b/yt/visualization/volume_rendering/image_handling.py
@@ -40,9 +40,7 @@
data["b"] = image[:,:,2]
data["a"] = image[:,:,3]
nx, ny = data["r"].shape
- fib = FITSImageBuffer(data, units="pixel",
- center=[0.5*(nx+1), 0.5*(ny+1)],
- scale=[1.]*2)
+ fib = FITSImageBuffer(data)
fib.writeto('%s.fits'%fn,clobber=True)
def import_rgba(name, h5=True):
Repository URL: https://bitbucket.org/yt_analysis/yt/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
More information about the yt-svn
mailing list