{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"yt has capabilities for writing 2D and 3D uniformly gridded data generated from datasets to FITS files. This is via the `FITSImageData` class. We'll test these capabilities out on an Athena dataset."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T14:59:13.253475Z",
"iopub.status.busy": "2023-10-10T14:59:13.253069Z",
"iopub.status.idle": "2023-10-10T14:59:14.550020Z",
"shell.execute_reply": "2023-10-10T14:59:14.549129Z"
}
},
"outputs": [],
"source": [
"import yt"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T14:59:14.554839Z",
"iopub.status.busy": "2023-10-10T14:59:14.554153Z",
"iopub.status.idle": "2023-10-10T14:59:27.210987Z",
"shell.execute_reply": "2023-10-10T14:59:27.209906Z"
}
},
"outputs": [],
"source": [
"units_override = {\n",
" \"length_unit\": (1.0, \"Mpc\"),\n",
" \"mass_unit\": (1.0e14, \"Msun\"),\n",
" \"time_unit\": (1.0, \"Myr\"),\n",
"}\n",
"ds = yt.load(\"MHDSloshing/virgo_low_res.0054.vtk\", units_override=units_override)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Creating FITS images from Slices and Projections"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are several ways to make a `FITSImageData` instance. The most intuitive ways are to use the `FITSSlice`, `FITSProjection`, `FITSOffAxisSlice`, and `FITSOffAxisProjection` classes to write slices and projections directly to FITS. To demonstrate a useful example of creating a FITS file, let's first make a `ProjectionPlot`:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T14:59:27.218509Z",
"iopub.status.busy": "2023-10-10T14:59:27.215369Z",
"iopub.status.idle": "2023-10-10T14:59:46.388524Z",
"shell.execute_reply": "2023-10-10T14:59:46.387712Z"
}
},
"outputs": [
{
"data": {
"text/html": [
"
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"prj = yt.ProjectionPlot(\n",
" ds,\n",
" \"z\",\n",
" (\"gas\", \"temperature\"),\n",
" weight_field=(\"gas\", \"density\"),\n",
" width=(500.0, \"kpc\"),\n",
")\n",
"prj.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Suppose that we wanted to write this projection to a FITS file for analysis and visualization in other programs, such as ds9. We can do that using `FITSProjection`:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T14:59:46.449544Z",
"iopub.status.busy": "2023-10-10T14:59:46.448530Z",
"iopub.status.idle": "2023-10-10T15:00:03.398912Z",
"shell.execute_reply": "2023-10-10T15:00:03.397987Z"
}
},
"outputs": [],
"source": [
"prj_fits = yt.FITSProjection(\n",
" ds, \"z\", (\"gas\", \"temperature\"), weight_field=(\"gas\", \"density\")\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"which took the same parameters as `ProjectionPlot` except the width, because `FITSProjection` and `FITSSlice` always make slices and projections of the width of the domain size, at the finest resolution available in the simulation, in a unit determined to be appropriate for the physical size of the dataset."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also set width manually in `FITSProjection`. For example, set the width to 500 kiloparsec to get FITS file of the same projection plot as discussed above."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:00:03.404705Z",
"iopub.status.busy": "2023-10-10T15:00:03.403225Z",
"iopub.status.idle": "2023-10-10T15:00:19.566367Z",
"shell.execute_reply": "2023-10-10T15:00:19.565528Z"
}
},
"outputs": [],
"source": [
"prj_fits = yt.FITSProjection(\n",
" ds,\n",
" \"z\",\n",
" (\"gas\", \"temperature\"),\n",
" weight_field=(\"gas\", \"density\"),\n",
" width=(500.0, \"kpc\"),\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you want the center coordinates of the image in either a slice or a projection to be (0,0) instead of the domain coordinates, set `origin=\"image\"`:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:00:19.570690Z",
"iopub.status.busy": "2023-10-10T15:00:19.570348Z",
"iopub.status.idle": "2023-10-10T15:00:34.566113Z",
"shell.execute_reply": "2023-10-10T15:00:34.565302Z"
}
},
"outputs": [],
"source": [
"prj_fits_img = yt.FITSProjection(\n",
" ds,\n",
" \"z\",\n",
" (\"gas\", \"temperature\"),\n",
" weight_field=(\"gas\", \"density\"),\n",
" width=(500.0, \"kpc\"),\n",
" origin=\"image\",\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false
},
"source": [
"## Making FITS images from Particle Projections"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false
},
"source": [
"To create a FITS image from a particle field which is smeared onto the image, we can use\n",
"`FITSParticleProjection`:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2023-10-10T15:00:34.570242Z",
"iopub.status.busy": "2023-10-10T15:00:34.569829Z",
"iopub.status.idle": "2023-10-10T15:00:36.123001Z",
"shell.execute_reply": "2023-10-10T15:00:36.121973Z"
}
},
"outputs": [],
"source": [
"dsp = yt.load(\"gizmo_64/output/snap_N64L16_135.hdf5\")\n",
"prjp_fits = yt.FITSParticleProjection(\n",
" dsp, \"x\", (\"PartType1\", \"particle_mass\"), deposition=\"cic\"\n",
")\n",
"prjp_fits.writeto(\"prjp.fits\", overwrite=True)"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false
},
"source": [
"Note that we used the \"Cloud-In-Cell\" interpolation method (`\"cic\"`) instead of the default\n",
"\"Nearest-Grid-Point\" (`\"ngp\"`) method. \n",
"\n",
"If you want the projection to be divided by the pixel area (to make a projection of mass density, \n",
"for example), supply the ``density`` keyword argument:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2023-10-10T15:00:36.127528Z",
"iopub.status.busy": "2023-10-10T15:00:36.126804Z",
"iopub.status.idle": "2023-10-10T15:00:36.320217Z",
"shell.execute_reply": "2023-10-10T15:00:36.318997Z"
}
},
"outputs": [],
"source": [
"prjpd_fits = yt.FITSParticleProjection(\n",
" dsp, \"x\", (\"PartType1\", \"particle_mass\"), density=True, deposition=\"cic\"\n",
")\n",
"prjpd_fits.writeto(\"prjpd.fits\", overwrite=True)"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false
},
"source": [
"`FITSParticleOffAxisProjection` can be used to make a projection along any arbitrary sight line:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2023-10-10T15:00:36.324489Z",
"iopub.status.busy": "2023-10-10T15:00:36.324014Z",
"iopub.status.idle": "2023-10-10T15:00:36.992644Z",
"shell.execute_reply": "2023-10-10T15:00:36.991822Z"
}
},
"outputs": [],
"source": [
"L = [1, -1, 1] # normal or \"line of sight\" vector\n",
"N = [0, 0, 1] # north or \"up\" vector\n",
"poff_fits = yt.FITSParticleOffAxisProjection(\n",
" dsp, L, (\"PartType1\", \"particle_mass\"), deposition=\"cic\", north_vector=N\n",
")\n",
"poff_fits.writeto(\"poff.fits\", overwrite=True)"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false
},
"source": [
"## Using `HDUList` Methods"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can call a number of the [AstroPy `HDUList`](https://astropy.readthedocs.io/en/latest/io/fits/api/hdulists.html) class's methods from a `FITSImageData` object. For example, `info` shows us the contents of the virtual FITS file:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:00:36.998041Z",
"iopub.status.busy": "2023-10-10T15:00:36.997567Z",
"iopub.status.idle": "2023-10-10T15:00:37.004735Z",
"shell.execute_reply": "2023-10-10T15:00:37.003657Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Filename: (No file associated with this FITSImageData)\n",
"No. Name Ver Type Cards Dimensions Format Units\n",
" 0 TEMPERATURE 1 PrimaryHDU 29 (512, 512) float64 K\n"
]
}
],
"source": [
"prj_fits.info()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also look at the header for a particular field:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:00:37.008971Z",
"iopub.status.busy": "2023-10-10T15:00:37.008446Z",
"iopub.status.idle": "2023-10-10T15:00:37.017743Z",
"shell.execute_reply": "2023-10-10T15:00:37.016652Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"SIMPLE = T / conforms to FITS standard \n",
"BITPIX = -64 / array data type \n",
"NAXIS = 2 / number of array dimensions \n",
"NAXIS1 = 512 \n",
"NAXIS2 = 512 \n",
"EXTEND = T \n",
"EXTNAME = 'TEMPERATURE' / extension name \n",
"BTYPE = 'temperature' \n",
"BUNIT = 'K ' \n",
"LUNIT = 1.0 / [kpc] \n",
"TUNIT = 1.0 / [Myr] \n",
"MUNIT = 100000000000000.0 / [Msun] \n",
"VUNIT = 1.0 / [Mpc/Myr] \n",
"BFUNIT = 0.02851538907227106 / [G] \n",
"TIME = 2700.111 \n",
"WCSAXES = 2 \n",
"CRPIX1 = 256.5 \n",
"CRPIX2 = 256.5 \n",
"CDELT1 = 0.9765625 \n",
"CDELT2 = 0.9765625 \n",
"CUNIT1 = 'kpc ' \n",
"CUNIT2 = 'kpc ' \n",
"CTYPE1 = 'LINEAR ' \n",
"CTYPE2 = 'LINEAR ' \n",
"CRVAL1 = 0.0 \n",
"CRVAL2 = 0.0 \n",
"LATPOLE = 90.0 \n",
"WCSNAME = 'yt ' \n",
"MJDREF = 0.0 "
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"prj_fits[\"temperature\"].header"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"where we can see that the units of the temperature field are Kelvin and the cell widths are in kiloparsecs. Note that the length, time, mass, velocity, and magnetic field units of the dataset have been copied into the header "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" If we want the raw image data with units, we can use the `data` attribute of this field:"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:00:37.022377Z",
"iopub.status.busy": "2023-10-10T15:00:37.021556Z",
"iopub.status.idle": "2023-10-10T15:00:37.032507Z",
"shell.execute_reply": "2023-10-10T15:00:37.031351Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"unyt_array([[34403626.80544101, 34403626.80544101, 34403626.80544101,\n",
" ..., 30453524.91959378, 30453524.91959378,\n",
" 30453524.91959378],\n",
" [34403626.80544101, 34403626.80544101, 34403626.80544101,\n",
" ..., 30453524.91959378, 30453524.91959378,\n",
" 30453524.91959378],\n",
" [34403626.80544101, 34403626.80544101, 34403626.80544101,\n",
" ..., 30453524.91959378, 30453524.91959378,\n",
" 30453524.91959378],\n",
" ...,\n",
" [35071597.64056484, 35071597.64056484, 35071597.64056484,\n",
" ..., 30673962.05243628, 30673962.05243628,\n",
" 30673962.05243628],\n",
" [35071597.64056484, 35071597.64056484, 35071597.64056484,\n",
" ..., 30673962.05243628, 30673962.05243628,\n",
" 30673962.05243628],\n",
" [35071597.64056484, 35071597.64056484, 35071597.64056484,\n",
" ..., 30673962.05243628, 30673962.05243628,\n",
" 30673962.05243628]], 'K')"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"prj_fits[\"temperature\"].data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Changing Aspects of the Images"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can use the `set_unit` method to change the units of a particular field:"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:00:37.037462Z",
"iopub.status.busy": "2023-10-10T15:00:37.036976Z",
"iopub.status.idle": "2023-10-10T15:00:37.049876Z",
"shell.execute_reply": "2023-10-10T15:00:37.048790Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"unyt_array([[61926528.24979382, 61926528.24979382, 61926528.24979382,\n",
" ..., 54816344.8552688 , 54816344.8552688 ,\n",
" 54816344.8552688 ],\n",
" [61926528.24979382, 61926528.24979382, 61926528.24979382,\n",
" ..., 54816344.8552688 , 54816344.8552688 ,\n",
" 54816344.8552688 ],\n",
" [61926528.24979382, 61926528.24979382, 61926528.24979382,\n",
" ..., 54816344.8552688 , 54816344.8552688 ,\n",
" 54816344.8552688 ],\n",
" ...,\n",
" [63128875.75301671, 63128875.75301671, 63128875.75301671,\n",
" ..., 55213131.69438529, 55213131.69438529,\n",
" 55213131.69438529],\n",
" [63128875.75301671, 63128875.75301671, 63128875.75301671,\n",
" ..., 55213131.69438529, 55213131.69438529,\n",
" 55213131.69438529],\n",
" [63128875.75301671, 63128875.75301671, 63128875.75301671,\n",
" ..., 55213131.69438529, 55213131.69438529,\n",
" 55213131.69438529]], 'R')"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"prj_fits.set_unit(\"temperature\", \"R\")\n",
"prj_fits[\"temperature\"].data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The length units of the image (and its coordinate system), as well as the resolution of the image, can be adjusted when creating it using the `length_unit` and `image_res` keyword arguments, respectively:"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:00:37.054091Z",
"iopub.status.busy": "2023-10-10T15:00:37.053516Z",
"iopub.status.idle": "2023-10-10T15:00:37.644693Z",
"shell.execute_reply": "2023-10-10T15:00:37.643885Z"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"WARNING: UnitsWarning: 'ly' did not parse as fits unit: At col 0, Unit 'ly' not supported by the FITS standard. Did you mean lyr? If this is meant to be a custom unit, define it with 'u.def_unit'. To have it recognized inside a file reader or other code, enable it with 'u.add_enabled_units'. For details, see https://docs.astropy.org/en/latest/units/combining_and_defining.html [astropy.units.core]\n"
]
}
],
"source": [
"# length_unit defaults to that from the dataset\n",
"# image_res defaults to 512\n",
"slc_fits = yt.FITSSlice(\n",
" ds, \"z\", (\"gas\", \"density\"), width=(500, \"kpc\"), length_unit=\"ly\", image_res=256\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can now check that this worked by looking at the header, notice in particular the `NAXIS[12]` and `CUNIT[12]` keywords (the `CDELT[12]` and `CRPIX[12]` values also change):"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:00:37.649273Z",
"iopub.status.busy": "2023-10-10T15:00:37.649036Z",
"iopub.status.idle": "2023-10-10T15:00:37.655337Z",
"shell.execute_reply": "2023-10-10T15:00:37.654369Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"SIMPLE = T / conforms to FITS standard \n",
"BITPIX = -64 / array data type \n",
"NAXIS = 2 / number of array dimensions \n",
"NAXIS1 = 256 \n",
"NAXIS2 = 256 \n",
"EXTEND = T \n",
"EXTNAME = 'DENSITY ' / extension name \n",
"BTYPE = 'density ' \n",
"BUNIT = 'g/cm**3 ' \n",
"LUNIT = 1.0 / [ly] \n",
"TUNIT = 1.0 / [Myr] \n",
"MUNIT = 100000000000000.0 / [Msun] \n",
"VUNIT = 1.0 / [Mpc/Myr] \n",
"BFUNIT = 0.02851538907227106 / [G] \n",
"TIME = 2700.111 \n",
"WCSAXES = 2 \n",
"CRPIX1 = 128.5 \n",
"CRPIX2 = 128.5 \n",
"CDELT1 = 6370.3778101354 \n",
"CDELT2 = 6370.3778101354 \n",
"CUNIT1 = 'ly ' \n",
"CUNIT2 = 'ly ' \n",
"CTYPE1 = 'LINEAR ' \n",
"CTYPE2 = 'LINEAR ' \n",
"CRVAL1 = 0.0 \n",
"CRVAL2 = 0.0 \n",
"LATPOLE = 90.0 \n",
"WCSNAME = 'yt ' \n",
"MJDREF = 0.0 "
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"slc_fits[\"density\"].header"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Saving and Loading Images"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The image can be written to disk using the `writeto` method:"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:00:37.659917Z",
"iopub.status.busy": "2023-10-10T15:00:37.659516Z",
"iopub.status.idle": "2023-10-10T15:00:37.670068Z",
"shell.execute_reply": "2023-10-10T15:00:37.669016Z"
}
},
"outputs": [],
"source": [
"prj_fits.writeto(\"sloshing.fits\", overwrite=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Since yt can read FITS image files, it can be loaded up just like any other dataset. Since we created this FITS file with `FITSImageData`, the image will contain information about the units and the current time of the dataset:"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:00:37.675129Z",
"iopub.status.busy": "2023-10-10T15:00:37.674721Z",
"iopub.status.idle": "2023-10-10T15:00:37.844492Z",
"shell.execute_reply": "2023-10-10T15:00:37.843343Z"
}
},
"outputs": [],
"source": [
"ds2 = yt.load(\"sloshing.fits\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"and we can make a `SlicePlot` of the 2D image, which shows the same data as the previous image:"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:00:37.850103Z",
"iopub.status.busy": "2023-10-10T15:00:37.849654Z",
"iopub.status.idle": "2023-10-10T15:00:39.363146Z",
"shell.execute_reply": "2023-10-10T15:00:39.361685Z"
}
},
"outputs": [
{
"data": {
"text/html": [
"
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"slc2 = yt.SlicePlot(ds2, \"z\", (\"gas\", \"temperature\"), width=(500.0, \"kpc\"))\n",
"slc2.set_log(\"temperature\", True)\n",
"slc2.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Creating `FITSImageData` Instances Directly from FRBs, PlotWindow instances, and 3D Grids"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you want more fine-grained control over what goes into the FITS file, you can call `FITSImageData` directly, with various kinds of inputs. For example, you could use a `FixedResolutionBuffer`, and specify you want the units in parsecs instead:"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:00:39.369406Z",
"iopub.status.busy": "2023-10-10T15:00:39.368887Z",
"iopub.status.idle": "2023-10-10T15:00:41.467802Z",
"shell.execute_reply": "2023-10-10T15:00:41.466508Z"
}
},
"outputs": [],
"source": [
"slc3 = ds.slice(0, 0.0)\n",
"frb = slc3.to_frb((500.0, \"kpc\"), 800)\n",
"fid_frb = frb.to_fits_data(\n",
" fields=[(\"gas\", \"density\"), (\"gas\", \"temperature\")], length_unit=\"pc\"\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If one creates a `PlotWindow` instance, e.g. `SlicePlot`, `ProjectionPlot`, etc., you can also call this same method there:"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:00:41.473464Z",
"iopub.status.busy": "2023-10-10T15:00:41.472977Z",
"iopub.status.idle": "2023-10-10T15:00:50.889788Z",
"shell.execute_reply": "2023-10-10T15:00:50.888869Z"
}
},
"outputs": [],
"source": [
"fid_pw = prj.to_fits_data(\n",
" fields=[(\"gas\", \"density\"), (\"gas\", \"temperature\")], length_unit=\"pc\"\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A 3D FITS cube can also be created from regularly gridded 3D data. In yt, there are covering grids and \"arbitrary grids\". The easiest way to make an arbitrary grid object is using `ds.r`, where we can index the dataset like a NumPy array, creating a grid of 1.0 Mpc on a side, centered on the origin, with 64 cells on a side:"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:00:50.897803Z",
"iopub.status.busy": "2023-10-10T15:00:50.897347Z",
"iopub.status.idle": "2023-10-10T15:00:59.426015Z",
"shell.execute_reply": "2023-10-10T15:00:59.425042Z"
}
},
"outputs": [],
"source": [
"grid = ds.r[\n",
" (-0.5, \"Mpc\"):(0.5, \"Mpc\"):64j,\n",
" (-0.5, \"Mpc\"):(0.5, \"Mpc\"):64j,\n",
" (-0.5, \"Mpc\"):(0.5, \"Mpc\"):64j,\n",
"]\n",
"fid_grid = grid.to_fits_data(\n",
" fields=[(\"gas\", \"density\"), (\"gas\", \"temperature\")], length_unit=\"Mpc\"\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Other `FITSImageData` Methods"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Creating Images from Others"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A `FITSImageData` instance can be generated from one previously written to disk using the `from_file` classmethod:"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:00:59.430021Z",
"iopub.status.busy": "2023-10-10T15:00:59.429545Z",
"iopub.status.idle": "2023-10-10T15:00:59.440155Z",
"shell.execute_reply": "2023-10-10T15:00:59.439466Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Filename: sloshing.fits\n",
"No. Name Ver Type Cards Dimensions Format Units\n",
" 0 TEMPERATURE 1 PrimaryHDU 29 (512, 512) float64 R\n"
]
}
],
"source": [
"fid = yt.FITSImageData.from_file(\"sloshing.fits\")\n",
"fid.info()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Multiple `FITSImageData` can be combined to create a new one, provided that the coordinate information is the same:"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:00:59.443942Z",
"iopub.status.busy": "2023-10-10T15:00:59.443385Z",
"iopub.status.idle": "2023-10-10T15:01:09.414773Z",
"shell.execute_reply": "2023-10-10T15:01:09.413999Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Filename: (No file associated with this FITSImageData)\n",
"No. Name Ver Type Cards Dimensions Format Units\n",
" 0 TEMPERATURE 1 PrimaryHDU 29 (512, 512) float64 R\n",
" 1 DENSITY 1 ImageHDU 30 (512, 512) float64 g/cm**2\n"
]
}
],
"source": [
"prj_fits2 = yt.FITSProjection(ds, \"z\", (\"gas\", \"density\"), width=(500.0, \"kpc\"))\n",
"prj_fits3 = yt.FITSImageData.from_images([prj_fits, prj_fits2])\n",
"prj_fits3.info()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Alternatively, individual fields can be popped as well to produce new instances of `FITSImageData`:"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:01:09.418329Z",
"iopub.status.busy": "2023-10-10T15:01:09.417809Z",
"iopub.status.idle": "2023-10-10T15:01:09.426814Z",
"shell.execute_reply": "2023-10-10T15:01:09.426063Z"
}
},
"outputs": [],
"source": [
"dens_fits = prj_fits3.pop(\"density\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So this new instance would only have the `\"density\"` field:"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:01:09.429850Z",
"iopub.status.busy": "2023-10-10T15:01:09.429585Z",
"iopub.status.idle": "2023-10-10T15:01:09.433473Z",
"shell.execute_reply": "2023-10-10T15:01:09.432809Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Filename: (No file associated with this FITSImageData)\n",
"No. Name Ver Type Cards Dimensions Format Units\n",
" 0 DENSITY 1 PrimaryHDU 28 (512, 512) float64 g/cm**2\n"
]
}
],
"source": [
"dens_fits.info()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"and the old one has the `\"density\"` field removed:"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:01:09.436317Z",
"iopub.status.busy": "2023-10-10T15:01:09.436041Z",
"iopub.status.idle": "2023-10-10T15:01:09.440247Z",
"shell.execute_reply": "2023-10-10T15:01:09.439558Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Filename: (No file associated with this FITSImageData)\n",
"No. Name Ver Type Cards Dimensions Format Units\n",
" 0 TEMPERATURE 1 PrimaryHDU 29 (512, 512) float64 R\n"
]
}
],
"source": [
"prj_fits3.info()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Adding Sky Coordinates to Images"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So far, the FITS images we have shown have linear spatial coordinates. We can see this by looking at the header for one of the fields, and examining the `CTYPE1` and `CTYPE2` keywords:"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:01:09.443072Z",
"iopub.status.busy": "2023-10-10T15:01:09.442804Z",
"iopub.status.idle": "2023-10-10T15:01:09.449895Z",
"shell.execute_reply": "2023-10-10T15:01:09.449176Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"SIMPLE = T / conforms to FITS standard \n",
"BITPIX = -64 / array data type \n",
"NAXIS = 2 / number of array dimensions \n",
"NAXIS1 = 512 \n",
"NAXIS2 = 512 \n",
"EXTEND = T \n",
"EXTNAME = 'TEMPERATURE' / extension name \n",
"BTYPE = 'temperature' \n",
"BUNIT = 'R ' \n",
"LUNIT = 1.0 / [kpc] \n",
"TUNIT = 1.0 / [Myr] \n",
"MUNIT = 100000000000000.0 / [Msun] \n",
"VUNIT = 1.0 / [Mpc/Myr] \n",
"BFUNIT = 0.02851538907227106 / [G] \n",
"TIME = 2700.111 \n",
"WCSAXES = 2 \n",
"CRPIX1 = 256.5 \n",
"CRPIX2 = 256.5 \n",
"CDELT1 = 0.9765625 \n",
"CDELT2 = 0.9765625 \n",
"CUNIT1 = 'kpc ' \n",
"CUNIT2 = 'kpc ' \n",
"CTYPE1 = 'LINEAR ' \n",
"CTYPE2 = 'LINEAR ' \n",
"CRVAL1 = 0.0 \n",
"CRVAL2 = 0.0 \n",
"LATPOLE = 90.0 \n",
"WCSNAME = 'yt ' \n",
"MJDREF = 0.0 "
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"prj_fits[\"temperature\"].header"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `WCSNAME` keyword is set to `\"yt\"` by default. \n",
"\n",
"However, one may want to take a projection of an object and make a crude mock observation out of it, with celestial coordinates. For this, we can use the `create_sky_wcs` method. Specify a center (RA, Dec) coordinate in degrees, as well as a linear scale in terms of angle per distance:"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:01:09.453059Z",
"iopub.status.busy": "2023-10-10T15:01:09.452800Z",
"iopub.status.idle": "2023-10-10T15:01:09.464338Z",
"shell.execute_reply": "2023-10-10T15:01:09.463677Z"
}
},
"outputs": [],
"source": [
"sky_center = [30.0, 45.0] # in degrees\n",
"sky_scale = (2.5, \"arcsec/kpc\") # could also use a YTQuantity\n",
"prj_fits.create_sky_wcs(sky_center, sky_scale, ctype=[\"RA---TAN\", \"DEC--TAN\"])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"By default, a tangent RA/Dec projection is used, but one could also use another projection using the `ctype` keyword. We can now look at the header and see it has the appropriate WCS now. The old `\"yt\"` WCS has been added to a second WCS in the header, where the parameters have an `\"A\"` appended to them:"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:01:09.467313Z",
"iopub.status.busy": "2023-10-10T15:01:09.467047Z",
"iopub.status.idle": "2023-10-10T15:01:09.472543Z",
"shell.execute_reply": "2023-10-10T15:01:09.471739Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"SIMPLE = T / conforms to FITS standard \n",
"BITPIX = -64 / array data type \n",
"NAXIS = 2 / number of array dimensions \n",
"NAXIS1 = 512 \n",
"NAXIS2 = 512 \n",
"EXTEND = T \n",
"EXTNAME = 'TEMPERATURE' / extension name \n",
"BTYPE = 'temperature' \n",
"BUNIT = 'R ' \n",
"LUNIT = 1.0 / [kpc] \n",
"TUNIT = 1.0 / [Myr] \n",
"MUNIT = 100000000000000.0 / [Msun] \n",
"VUNIT = 1.0 / [Mpc/Myr] \n",
"BFUNIT = 0.02851538907227106 / [G] \n",
"TIME = 2700.111 \n",
"WCSAXES = 2 \n",
"CRPIX1 = 256.5 \n",
"CRPIX2 = 256.5 \n",
"CDELT1 = -0.00067816840277778 \n",
"CDELT2 = 0.00067816840277778 \n",
"CUNIT1 = 'deg ' \n",
"CUNIT2 = 'deg ' \n",
"CTYPE1 = 'RA---TAN' \n",
"CTYPE2 = 'DEC--TAN' \n",
"CRVAL1 = 30.0 \n",
"CRVAL2 = 45.0 \n",
"LATPOLE = 45.0 \n",
"WCSNAME = 'celestial' \n",
"MJDREF = 0.0 \n",
"LONPOLE = 180.0 \n",
"RADESYS = 'ICRS ' \n",
"WCSAXESA= 2 \n",
"CRPIX1A = 256.5 \n",
"CRPIX2A = 256.5 \n",
"CDELT1A = 0.9765625 \n",
"CDELT2A = 0.9765625 \n",
"CUNIT1A = 'kpc ' \n",
"CUNIT2A = 'kpc ' \n",
"CTYPE1A = 'LINEAR ' \n",
"CTYPE2A = 'LINEAR ' \n",
"CRVAL1A = 0.0 \n",
"CRVAL2A = 0.0 \n",
"LATPOLEA= 90.0 \n",
"WCSNAMEA= 'yt ' \n",
"MJDREFA = 0.0 "
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"prj_fits[\"temperature\"].header"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"and now the `WCSNAME` has been set to `\"celestial\"`. If you want the original WCS to remain in the original place, then you can make the call to `create_sky_wcs` and set `replace_old_wcs=False`, which will put the new, celestial WCS in the second one:"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"collapsed": true,
"execution": {
"iopub.execute_input": "2023-10-10T15:01:09.476170Z",
"iopub.status.busy": "2023-10-10T15:01:09.475947Z",
"iopub.status.idle": "2023-10-10T15:01:09.482502Z",
"shell.execute_reply": "2023-10-10T15:01:09.481875Z"
}
},
"outputs": [],
"source": [
"prj_fits3.create_sky_wcs(\n",
" sky_center, sky_scale, ctype=[\"RA---TAN\", \"DEC--TAN\"], replace_old_wcs=False\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:01:09.485872Z",
"iopub.status.busy": "2023-10-10T15:01:09.485379Z",
"iopub.status.idle": "2023-10-10T15:01:09.491100Z",
"shell.execute_reply": "2023-10-10T15:01:09.490381Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"SIMPLE = T / conforms to FITS standard \n",
"BITPIX = -64 / array data type \n",
"NAXIS = 2 / number of array dimensions \n",
"NAXIS1 = 512 \n",
"NAXIS2 = 512 \n",
"EXTEND = T \n",
"EXTNAME = 'TEMPERATURE' / extension name \n",
"BTYPE = 'temperature' \n",
"BUNIT = 'R ' \n",
"LUNIT = 1.0 / [kpc] \n",
"TUNIT = 1.0 / [Myr] \n",
"MUNIT = 100000000000000.0 / [Msun] \n",
"VUNIT = 1.0 / [Mpc/Myr] \n",
"BFUNIT = 0.02851538907227106 / [G] \n",
"TIME = 2700.111 \n",
"WCSAXES = 2 \n",
"CRPIX1 = 256.5 \n",
"CRPIX2 = 256.5 \n",
"CDELT1 = 0.9765625 \n",
"CDELT2 = 0.9765625 \n",
"CUNIT1 = 'kpc ' \n",
"CUNIT2 = 'kpc ' \n",
"CTYPE1 = 'LINEAR ' \n",
"CTYPE2 = 'LINEAR ' \n",
"CRVAL1 = 0.0 \n",
"CRVAL2 = 0.0 \n",
"LATPOLE = 90.0 \n",
"WCSNAME = 'yt ' \n",
"MJDREF = 0.0 \n",
"WCSAXESA= 2 \n",
"CRPIX1A = 256.5 \n",
"CRPIX2A = 256.5 \n",
"CDELT1A = -0.00067816840277778 \n",
"CDELT2A = 0.00067816840277778 \n",
"CUNIT1A = 'deg ' \n",
"CUNIT2A = 'deg ' \n",
"CTYPE1A = 'RA---TAN' \n",
"CTYPE2A = 'DEC--TAN' \n",
"CRVAL1A = 30.0 \n",
"CRVAL2A = 45.0 \n",
"LONPOLEA= 180.0 \n",
"LATPOLEA= 45.0 \n",
"WCSNAMEA= 'celestial' \n",
"MJDREFA = 0.0 \n",
"RADESYSA= 'ICRS ' "
]
},
"execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"prj_fits3[\"temperature\"].header"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Updating Header Parameters"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also add header keywords to a single field or for all fields in the FITS image using `update_header`:"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:01:09.493674Z",
"iopub.status.busy": "2023-10-10T15:01:09.493428Z",
"iopub.status.idle": "2023-10-10T15:01:09.497022Z",
"shell.execute_reply": "2023-10-10T15:01:09.496313Z"
}
},
"outputs": [],
"source": [
"fid_frb.update_header(\"all\", \"time\", 0.1) # Update all the fields\n",
"fid_frb.update_header(\"temperature\", \"scale\", \"Rankine\") # Update just one field"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:01:09.500354Z",
"iopub.status.busy": "2023-10-10T15:01:09.500104Z",
"iopub.status.idle": "2023-10-10T15:01:09.504048Z",
"shell.execute_reply": "2023-10-10T15:01:09.503433Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.1\n",
"Rankine\n"
]
}
],
"source": [
"print(fid_frb[\"density\"].header[\"time\"])\n",
"print(fid_frb[\"temperature\"].header[\"scale\"])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Changing Image Names"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can use the `change_image_name` method to change the name of an image in a `FITSImageData` instance:"
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:01:09.506484Z",
"iopub.status.busy": "2023-10-10T15:01:09.506236Z",
"iopub.status.idle": "2023-10-10T15:01:09.510267Z",
"shell.execute_reply": "2023-10-10T15:01:09.509603Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Filename: (No file associated with this FITSImageData)\n",
"No. Name Ver Type Cards Dimensions Format Units\n",
" 0 MASS_PER_VOLUME 1 PrimaryHDU 29 (800, 800) float64 g/cm**3\n",
" 1 TEMPERATURE 1 ImageHDU 31 (800, 800) float64 K\n"
]
}
],
"source": [
"fid_frb.change_image_name(\"density\", \"mass_per_volume\")\n",
"fid_frb.info() # now \"density\" should be gone and \"mass_per_volume\" should be in its place"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Convolving FITS Images"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally, you can convolve an image inside a `FITSImageData` instance with a kernel, either a Gaussian with a specific standard deviation, or any kernel provided by AstroPy. See AstroPy's [Convolution and filtering](http://docs.astropy.org/en/stable/convolution/index.html) for more details."
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:01:09.513516Z",
"iopub.status.busy": "2023-10-10T15:01:09.513074Z",
"iopub.status.idle": "2023-10-10T15:01:10.207405Z",
"shell.execute_reply": "2023-10-10T15:01:10.204664Z"
}
},
"outputs": [],
"source": [
"dens_fits.writeto(\"not_convolved.fits\", overwrite=True)\n",
"# Gaussian kernel with standard deviation of 3.0 kpc\n",
"dens_fits.convolve(\"density\", 3.0)\n",
"dens_fits.writeto(\"convolved.fits\", overwrite=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now let's load these up as datasets and see the difference:"
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:01:10.212441Z",
"iopub.status.busy": "2023-10-10T15:01:10.211099Z",
"iopub.status.idle": "2023-10-10T15:01:10.407362Z",
"shell.execute_reply": "2023-10-10T15:01:10.406504Z"
}
},
"outputs": [],
"source": [
"ds0 = yt.load(\"not_convolved.fits\")\n",
"dsc = yt.load(\"convolved.fits\")"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:01:10.411211Z",
"iopub.status.busy": "2023-10-10T15:01:10.410916Z",
"iopub.status.idle": "2023-10-10T15:01:11.780593Z",
"shell.execute_reply": "2023-10-10T15:01:11.779921Z"
}
},
"outputs": [
{
"data": {
"text/html": [
"
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"slc3 = yt.SlicePlot(ds0, \"z\", (\"gas\", \"density\"), width=(500.0, \"kpc\"))\n",
"slc3.set_log(\"density\", True)\n",
"slc3.show()"
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {
"execution": {
"iopub.execute_input": "2023-10-10T15:01:11.784120Z",
"iopub.status.busy": "2023-10-10T15:01:11.783766Z",
"iopub.status.idle": "2023-10-10T15:01:13.311339Z",
"shell.execute_reply": "2023-10-10T15:01:13.310493Z"
}
},
"outputs": [
{
"data": {
"text/html": [
"
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"slc4 = yt.SlicePlot(dsc, \"z\", (\"gas\", \"density\"), width=(500.0, \"kpc\"))\n",
"slc4.set_log(\"density\", True)\n",
"slc4.show()"
]
}
],
"metadata": {
"anaconda-cloud": {},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}